<!DOCTYPE html>
<html lang="zh-CN">
    <!-- title -->




<!-- keywords -->




<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" >
    <meta name="author" content="HAOJX">
    <meta name="renderer" content="webkit">
    <meta name="copyright" content="HAOJX">
    
    <meta name="keywords" content="hexo,hexo-theme,docker,kubernetes,Linux,运维">
    
    <meta name="description" content="">
    <meta http-equiv="Cache-control" content="no-cache">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <title>kubernetes CRD下篇--编写自定义控制器 · HAOJX |郝建勋的笔记与博客</title>
    <style type="text/css">
    @font-face {
        font-family: 'Oswald-Regular';
        src: url("/font/Oswald-Regular.ttf");
    }

    body {
        margin: 0;
    }

    header,
    footer,
    .back-top,
    .sidebar,
    .container,
    .site-intro-meta,
    .toc-wrapper {
        display: none;
    }

    .site-intro {
        position: relative;
        z-index: 3;
        width: 100%;
        /* height: 50vh; */
        overflow: hidden;
    }

    .site-intro-placeholder {
        position: absolute;
        z-index: -2;
        top: 0;
        left: 0;
        width: calc(100% + 300px);
        height: 100%;
        background: repeating-linear-gradient(-45deg, #444 0, #444 80px, #333 80px, #333 160px);
        background-position: center center;
        transform: translate3d(-226px, 0, 0);
        animation: gradient-move 2.5s ease-out 0s infinite;
    }

    @keyframes gradient-move {
        0% {
            transform: translate3d(-226px, 0, 0);
        }
        100% {
            transform: translate3d(0, 0, 0);
        }
    }

</style>

    <link rel="preload" href= "/css/style.css?v=20180824" as="style" onload="this.onload=null;this.rel='stylesheet'" />
    <link rel="stylesheet" href= "/css/mobile.css?v=20180824" media="(max-width: 980px)">
    
    <link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.2.5/jquery.fancybox.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'" />
    
    <!-- /*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
/* This file is meant as a standalone workflow for
- testing support for link[rel=preload]
- enabling async CSS loading in browsers that do not support rel=preload
- applying rel preload css once loaded, whether supported or not.
*/ -->
<script>
(function( w ){
	"use strict";
	// rel=preload support test
	if( !w.loadCSS ){
		w.loadCSS = function(){};
	}
	// define on the loadCSS obj
	var rp = loadCSS.relpreload = {};
	// rel=preload feature support test
	// runs once and returns a function for compat purposes
	rp.support = (function(){
		var ret;
		try {
			ret = w.document.createElement( "link" ).relList.supports( "preload" );
		} catch (e) {
			ret = false;
		}
		return function(){
			return ret;
		};
	})();

	// if preload isn't supported, get an asynchronous load by using a non-matching media attribute
	// then change that media back to its intended value on load
	rp.bindMediaToggle = function( link ){
		// remember existing media attr for ultimate state, or default to 'all'
		var finalMedia = link.media || "all";

		function enableStylesheet(){
			link.media = finalMedia;
		}

		// bind load handlers to enable media
		if( link.addEventListener ){
			link.addEventListener( "load", enableStylesheet );
		} else if( link.attachEvent ){
			link.attachEvent( "onload", enableStylesheet );
		}

		// Set rel and non-applicable media type to start an async request
		// note: timeout allows this to happen async to let rendering continue in IE
		setTimeout(function(){
			link.rel = "stylesheet";
			link.media = "only x";
		});
		// also enable media after 3 seconds,
		// which will catch very old browsers (android 2.x, old firefox) that don't support onload on link
		setTimeout( enableStylesheet, 3000 );
	};

	// loop through link elements in DOM
	rp.poly = function(){
		// double check this to prevent external calls from running
		if( rp.support() ){
			return;
		}
		var links = w.document.getElementsByTagName( "link" );
		for( var i = 0; i < links.length; i++ ){
			var link = links[ i ];
			// qualify links to those with rel=preload and as=style attrs
			if( link.rel === "preload" && link.getAttribute( "as" ) === "style" && !link.getAttribute( "data-loadcss" ) ){
				// prevent rerunning on link
				link.setAttribute( "data-loadcss", true );
				// bind listeners to toggle media back
				rp.bindMediaToggle( link );
			}
		}
	};

	// if unsupported, run the polyfill
	if( !rp.support() ){
		// run once at least
		rp.poly();

		// rerun poly on an interval until onload
		var run = w.setInterval( rp.poly, 500 );
		if( w.addEventListener ){
			w.addEventListener( "load", function(){
				rp.poly();
				w.clearInterval( run );
			} );
		} else if( w.attachEvent ){
			w.attachEvent( "onload", function(){
				rp.poly();
				w.clearInterval( run );
			} );
		}
	}


	// commonjs
	if( typeof exports !== "undefined" ){
		exports.loadCSS = loadCSS;
	}
	else {
		w.loadCSS = loadCSS;
	}
}( typeof global !== "undefined" ? global : this ) );
</script>

    <link rel="icon" href= "/assets/favicon.ico" />
    <link rel="preload" href="https://cdn.jsdelivr.net/npm/webfontloader@1.6.28/webfontloader.min.js" as="script" />
    <link rel="preload" href="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js" as="script" />
    <link rel="preload" href="/scripts/main.js" as="script" />
    <link rel="preload" as="font" href="/font/Oswald-Regular.ttf" crossorigin>
    <link rel="preload" as="font" href="https://at.alicdn.com/t/font_327081_1dta1rlogw17zaor.woff" crossorigin>
    
    <!-- fancybox -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.2.5/jquery.fancybox.min.js" defer></script>
    <!-- 百度统计  -->
    
    <!-- 谷歌统计  -->
    
</head>

    
        <body class="post-body">
    
    
<header class="header">

    <div class="read-progress"></div>
    <div class="header-sidebar-menu">&#xe775;</div>
    <!-- post页的toggle banner  -->
    
    <div class="banner">
            <div class="blog-title">
                <a href="/" >HAOJX</a>
            </div>
            <div class="post-title">
                <a href="#" class="post-name">kubernetes CRD下篇--编写自定义控制器</a>
            </div>
    </div>
    
    <a class="home-link" href=/>HAOJX</a>
</header>
    <div class="wrapper">
        <div class="site-intro" style="







height:50vh;
">
    
    <!-- 主页  -->
    
    
    <!-- 404页  -->
            
    <div class="site-intro-placeholder"></div>
    <div class="site-intro-img" style="background-image: url(https://source.unsplash.com/collection/770996/2100x900)"></div>
    <div class="site-intro-meta">
        <!-- 标题  -->
        <h1 class="intro-title">
            <!-- 主页  -->
            
            kubernetes CRD下篇--编写自定义控制器
            <!-- 404 -->
            
        </h1>
        <!-- 副标题 -->
        <p class="intro-subtitle">
            <!-- 主页副标题  -->
            
            
            <!-- 404 -->
            
        </p>
        <!-- 文章页meta -->
        
            <div class="post-intros">
                <!-- 文章页标签  -->
                
                    <div class= post-intro-tags >
    
        <a class="post-tag" href="javascript:void(0);" data-tags = "kubernetes">kubernetes</a>
    
</div>
                
                
                    <div class="post-intro-read">
                        <span>字数统计: <span class="post-count word-count">7.8k</span>阅读时长: <span class="post-count reading-time">33 min</span></span>
                    </div>
                
                <div class="post-intro-meta">
                    <span class="post-intro-calander iconfont-archer">&#xe676;</span>
                    <span class="post-intro-time">2018/11/10</span>
                    
                    <span id="busuanzi_container_page_pv" class="busuanzi-pv">
                        <span class="iconfont-archer">&#xe602;</span>
                        <span id="busuanzi_value_page_pv"></span>
                    </span>
                    
                    <span class="shareWrapper">
                        <span class="iconfont-archer shareIcon">&#xe71d;</span>
                        <span class="shareText">Share</span>
                        <ul class="shareList">
                            <li class="iconfont-archer share-qr" data-type="qr">&#xe75b;
                                <div class="share-qrcode"></div>
                            </li>
                            <li class="iconfont-archer" data-type="weibo">&#xe619;</li>
                            <li class="iconfont-archer" data-type="qzone">&#xe62e;</li>
                            <li class="iconfont-archer" data-type="twitter">&#xe634;</li>
                            <li class="iconfont-archer" data-type="facebook">&#xe67a;</li>
                        </ul>
                    </span>
                </div>
            </div>
        
    </div>
</div>
        <script>
 
  // get user agent
  var browser = {
    versions: function () {
      var u = window.navigator.userAgent;
      return {
        userAgent: u,
        trident: u.indexOf('Trident') > -1, //IE内核
        presto: u.indexOf('Presto') > -1, //opera内核
        webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
        gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
        mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
        ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
        android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器
        iPhone: u.indexOf('iPhone') > -1 || u.indexOf('Mac') > -1, //是否为iPhone或者安卓QQ浏览器
        iPad: u.indexOf('iPad') > -1, //是否为iPad
        webApp: u.indexOf('Safari') == -1, //是否为web应用程序，没有头部与底部
        weixin: u.indexOf('MicroMessenger') == -1, //是否为微信浏览器
        uc: u.indexOf('UCBrowser') > -1 //是否为android下的UC浏览器
      };
    }()
  }
  console.log("userAgent:" + browser.versions.userAgent);

  // callback
  function fontLoaded() {
    console.log('font loaded');
    if (document.getElementsByClassName('site-intro-meta')) {
      document.getElementsByClassName('intro-title')[0].classList.add('intro-fade-in');
      document.getElementsByClassName('intro-subtitle')[0].classList.add('intro-fade-in');
      var postIntros = document.getElementsByClassName('post-intros')[0]
      if (postIntros) {
        postIntros.classList.add('post-fade-in');
      }
    }
  }

  // UC不支持跨域，所以直接显示
  function asyncCb(){
    if (browser.versions.uc) {
      console.log("UCBrowser");
      fontLoaded();
    } else {
      WebFont.load({
        custom: {
          families: ['Oswald-Regular']
        },
        loading: function () {  //所有字体开始加载
          // console.log('loading');
        },
        active: function () {  //所有字体已渲染
          fontLoaded();
        },
        inactive: function () { //字体预加载失败，无效字体或浏览器不支持加载
          console.log('inactive: timeout');
          fontLoaded();
        },
        timeout: 5000 // Set the timeout to two seconds
      });
    }
  }

  function asyncErr(){
    console.warn('script load from CDN failed, will load local script')
  }

  // load webfont-loader async, and add callback function
  function async(u, cb, err) {
    var d = document, t = 'script',
      o = d.createElement(t),
      s = d.getElementsByTagName(t)[0];
    o.src = u;
    if (cb) { o.addEventListener('load', function (e) { cb(null, e); }, false); }
    if (err) { o.addEventListener('error', function (e) { err(null, e); }, false); }
    s.parentNode.insertBefore(o, s);
  }

  var asyncLoadWithFallBack = function(arr, success, reject) {
      var currReject = function(){
        reject()
        arr.shift()
        if(arr.length)
          async(arr[0], success, currReject)
        }

      async(arr[0], success, currReject)
  }

  asyncLoadWithFallBack([
    "https://cdn.jsdelivr.net/npm/webfontloader@1.6.28/webfontloader.min.js", 
    "https://cdn.bootcss.com/webfont/1.6.28/webfontloader.js",
    "/lib/webfontloader.min.js"
  ], asyncCb, asyncErr)
</script>        
        <img class="loading" src="/assets/loading.svg" style="display: block; margin: 6rem auto 0 auto; width: 6rem; height: 6rem;" />
        <div class="container container-unloaded">
            <main class="main post-page">
    <article class="article-entry">
        <h2 id="自定义控制器的工作流程"><a href="#自定义控制器的工作流程" class="headerlink" title="自定义控制器的工作流程"></a>自定义控制器的工作流程</h2><p><img src="https://i.loli.net/2018/11/21/5bf4db97d1448.png" alt="kubernetes的自定义控制器流程.png"></p>
<p>通过Informer , 从 Kubernetes 的 APIServer 里获取它所关心的对象 , Informer 与 API 对象是一一对应的 , 我们下面的例子的informer就是 Network 对象的 Informer（Network Informer）</p>
<p>而informer通过这个对象资源对象的client的对象来传递参数, 并和APIServer维持连接 , 使用的是一个叫Reflector 包 , client通过<strong>ListAndWatch</strong> 的方法来”获取”和”监听”要获取对象的变化</p>
<p>在 ListAndWatch 机制下，一旦 APIServer 端有新的 Network 实例被创建、删除或者更新，Reflector 都会收到“事件通知”。这时，该事件及它对应的 API 对象这个组合，就被称为增量（Delta），它会被放进一个 Delta FIFO Queue（即：增量先进先出队列）中。</p>
<p>而另一方面，Informe 会不断地从这个 Delta FIFO Queue 里读取（Pop）增量。每拿到一个增量，Informer 就会判断这个增量里的事件类型，然后创建或者更新本地对象的缓存。这个缓存，在 Kubernetes 里一般被叫作 Store。</p>
<p>比如，如果事件类型是 Added（添加对象），那么 Informer 就会通过一个叫作 Indexer 的库把这个增量里的 API 对象保存在本地缓存中，并为它创建索引。相反地，如果增量的事件类型是 Deleted（删除对象），那么 Informer 就会从本地缓存中删除这个对象。</p>
<p>这个<strong>同步本地缓存的工作，是 Informer 的第一个职责，也是它最重要的职责。</strong></p>
<p>而<strong>Informer 的第二个职责，则是根据这些事件的类型，触发事先注册好的 ResourceEventHandler</strong>。这些 Handler，需要在创建控制器的时候注册给它对应的 Informer</p>
<h2 id="例子1"><a href="#例子1" class="headerlink" title="例子1"></a>例子1</h2><p>接下来，我们就来编写这个控制器的定义，它的主要内容如下所示：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewController</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">  kubeclientset kubernetes.Interface,</span></span></span><br><span class="line"><span class="function"><span class="params">  networkclientset clientset.Interface,</span></span></span><br><span class="line"><span class="function"><span class="params">  networkInformer informers.NetworkInformer)</span> *<span class="title">Controller</span></span> &#123;</span><br><span class="line">  ...</span><br><span class="line">  controller := &amp;Controller&#123;</span><br><span class="line">    kubeclientset:    kubeclientset,</span><br><span class="line">    networkclientset: networkclientset,</span><br><span class="line">    networksLister:   networkInformer.Lister(),</span><br><span class="line">    networksSynced:   networkInformer.Informer().HasSynced,</span><br><span class="line">    workqueue:        workqueue.NewNamedRateLimitingQueue(...,  <span class="string">"Networks"</span>),</span><br><span class="line">    ...</span><br><span class="line">  &#125;</span><br><span class="line">    networkInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs&#123;</span><br><span class="line">    AddFunc: controller.enqueueNetwork,</span><br><span class="line">    UpdateFunc: <span class="function"><span class="keyword">func</span><span class="params">(old, <span class="built_in">new</span> <span class="keyword">interface</span>&#123;&#125;)</span></span> &#123;</span><br><span class="line">      oldNetwork := old.(*samplecrdv1.Network)</span><br><span class="line">      newNetwork := <span class="built_in">new</span>.(*samplecrdv1.Network)</span><br><span class="line">      <span class="keyword">if</span> oldNetwork.ResourceVersion == newNetwork.ResourceVersion &#123;</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">      &#125;</span><br><span class="line">      controller.enqueueNetwork(<span class="built_in">new</span>)</span><br><span class="line">    &#125;,</span><br><span class="line">    DeleteFunc: controller.enqueueNetworkForDelete,</span><br><span class="line"> <span class="keyword">return</span> controller</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>我前面在 main 函数里创建了两个 client（kubeclientset 和 networkclientset），然后在这段代码里，使用这两个 client 和前面创建的 Informer，初始化了自定义控制器。</strong></p>
<p>值得注意的是，在这个自定义控制器里，我还设置了一个工作队列（work queue），它正是处于示意图中间位置的 WorkQueue。这个工作队列的作用是，负责同步 Informer 和控制循环之间的数据。</p>
<blockquote>
<p>实际上，Kubernetes 项目为我们提供了很多个工作队列的实现，你可以根据需要选择合适的库直接使用。</p>
</blockquote>
<p><strong>然后，我为 networkInformer 注册了三个 Handler（AddFunc、UpdateFunc 和 DeleteFunc），分别对应 API 对象的“添加”“更新”和“删除”事件。而具体的处理操作，都是将该事件对应的 API 对象加入到工作队列中。</strong></p>
<p>需要注意的是，实际入队的并不是 API 对象本身，而是它们的 Key，即：该 API 对象的/。</p>
<p>而我们后面即将编写的控制循环，则会不断地从这个工作队列里拿到这些 Key，然后开始执行真正的控制逻辑。</p>
<p>综合上面的讲述，你现在应该就能明白，<strong>所谓 Informer，其实就是一个带有本地缓存和索引机制的、可以注册 EventHandler 的 client</strong>。它是自定义控制器跟 APIServer 进行数据同步的重要组件。</p>
<p>更具体地说，Informer 通过一种叫作 ListAndWatch 的方法，把 APIServer 中的 API 对象缓存在了本地，并负责更新和维护这个缓存。</p>
<p>其中，ListAndWatch 方法的含义是：首先，通过 APIServer 的 LIST API“获取”所有最新版本的 API 对象；然后，再通过 WATCH API 来“监听”所有这些 API 对象的变化。</p>
<p>而通过监听到的事件变化，Informer 就可以实时地更新本地缓存，并且调用这些事件对应的 EventHandler 了。</p>
<p>此外，在这个过程中，每经过 resyncPeriod 指定的时间，Informer 维护的本地缓存，都会使用最近一次 LIST 返回的结果强制更新一次，从而保证缓存的有效性。在 Kubernetes 中，这个缓存强制更新的操作就叫作：resync。</p>
<p>需要注意的是，这个定时 resync 操作，也会触发 Informer 注册的“更新”事件。但此时，这个“更新”事件对应的 Network 对象实际上并没有发生变化，即：新、旧两个 Network 对象的 ResourceVersion 是一样的。在这种情况下，Informer 就不需要对这个更新事件再做进一步的处理了。</p>
<p>这也是为什么我在上面的 UpdateFunc 方法里，先判断了一下新、旧两个 Network 对象的版本（ResourceVersion）是否发生了变化，然后才开始进行的入队操作。</p>
<p>以上，就是 Kubernetes 中的 Informer 库的工作原理了。</p>
<p>接下来，我们就来到了示意图中最后面的控制循环（Control Loop）部分，也正是我在 main 函数最后调用 controller.Run() 启动的“控制循环”。它的主要内容如下所示：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Controller)</span> <span class="title">Run</span><span class="params">(threadiness <span class="keyword">int</span>, stopCh &lt;-<span class="keyword">chan</span> <span class="keyword">struct</span>&#123;&#125;)</span> <span class="title">error</span></span> &#123;</span><br><span class="line"> ...</span><br><span class="line">  <span class="keyword">if</span> ok := cache.WaitForCacheSync(stopCh, c.networksSynced); !ok &#123;</span><br><span class="line">    <span class="keyword">return</span> fmt.Errorf(<span class="string">"failed to wait for caches to sync"</span>)</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">for</span> i := <span class="number">0</span>; i &lt; threadiness; i++ &#123;</span><br><span class="line">    <span class="keyword">go</span> wait.Until(c.runWorker, time.Second, stopCh)</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>可以看到，启动控制循环的逻辑非常简单：</p>
<ul>
<li>首先，等待 Informer 完成一次本地缓存的数据同步操作；</li>
<li>然后，直接通过 goroutine 启动一个（或者并发启动多个）“无限循环”的任务。</li>
</ul>
<p>而这个“无限循环”任务的每一个循环周期，执行的正是我们真正关心的业务逻辑。</p>
<p>所以接下来，我们就来编写这个自定义控制器的业务逻辑，它的主要内容如下所示：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Controller)</span> <span class="title">runWorker</span><span class="params">()</span></span> &#123;</span><br><span class="line">  <span class="keyword">for</span> c.processNextWorkItem() &#123;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Controller)</span> <span class="title">processNextWorkItem</span><span class="params">()</span> <span class="title">bool</span></span> &#123;</span><br><span class="line">  obj, shutdown := c.workqueue.Get()</span><br><span class="line">  </span><br><span class="line">  ...</span><br><span class="line">  </span><br><span class="line">  err := <span class="function"><span class="keyword">func</span><span class="params">(obj <span class="keyword">interface</span>&#123;&#125;)</span> <span class="title">error</span></span> &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">if</span> err := c.syncHandler(key); err != <span class="literal">nil</span> &#123;</span><br><span class="line">     <span class="keyword">return</span> fmt.Errorf(<span class="string">"error syncing '%s': %s"</span>, key, err.Error())</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    c.workqueue.Forget(obj)</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">  &#125;(obj)</span><br><span class="line">  </span><br><span class="line">  ...</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Controller)</span> <span class="title">syncHandler</span><span class="params">(key <span class="keyword">string</span>)</span> <span class="title">error</span></span> &#123;</span><br><span class="line"> </span><br><span class="line">  namespace, name, err := cache.SplitMetaNamespaceKey(key)</span><br><span class="line">  ...</span><br><span class="line">  </span><br><span class="line">  network, err := c.networksLister.Networks(namespace).Get(name)</span><br><span class="line">  <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> errors.IsNotFound(err) &#123;</span><br><span class="line">      glog.Warningf(<span class="string">"Network does not exist in local cache: %s/%s, will delete it from Neutron ..."</span>,</span><br><span class="line">      namespace, name)</span><br><span class="line">      </span><br><span class="line">      glog.Warningf(<span class="string">"Network: %s/%s does not exist in local cache, will delete it from Neutron ..."</span>,</span><br><span class="line">    namespace, name)</span><br><span class="line">    </span><br><span class="line">     <span class="comment">// FIX ME: call Neutron API to delete this network by name.</span></span><br><span class="line">     <span class="comment">//</span></span><br><span class="line">     <span class="comment">// neutron.Delete(namespace, name)</span></span><br><span class="line">     </span><br><span class="line">     <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">  &#125;</span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> err</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  glog.Infof(<span class="string">"[Neutron] Try to process network: %#v ..."</span>, network)</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// FIX ME: Do diff().</span></span><br><span class="line">  <span class="comment">//</span></span><br><span class="line">  <span class="comment">// actualNetwork, exists := neutron.Get(namespace, name)</span></span><br><span class="line">  <span class="comment">//</span></span><br><span class="line">  <span class="comment">// if !exists &#123;</span></span><br><span class="line">  <span class="comment">//   neutron.Create(namespace, name)</span></span><br><span class="line">  <span class="comment">// &#125; else if !reflect.DeepEqual(actualNetwork, network) &#123;</span></span><br><span class="line">  <span class="comment">//   neutron.Update(namespace, name)</span></span><br><span class="line">  <span class="comment">// &#125;</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>可以看到，在这个执行周期里（processNextWorkItem），我们<strong>首先</strong>从工作队列里出队（workqueue.Get）了一个成员，也就是一个 Key（Network 对象的：namespace/name）。</p>
<p><strong>然后</strong>，在 syncHandler 方法中，我使用这个 Key，尝试从 Informer 维护的缓存中拿到了它所对应的 Network 对象。</p>
<p>可以看到，在这里，我使用了 networksLister 来尝试获取这个 Key 对应的 Network 对象。这个操作，其实就是在访问本地缓存的索引。实际上，在 Kubernetes 的源码中，你会经常看到控制器从各种 Lister 里获取对象，比如：podLister、nodeLister 等等，它们使用的都是 Informer 和缓存机制。</p>
<p>而如果控制循环从缓存中拿不到这个对象（即：networkLister 返回了 IsNotFound 错误），那就意味着这个 Network 对象的 Key 是通过前面的“删除”事件添加进工作队列的。所以，尽管队列里有这个 Key，但是对应的 Network 对象已经被删除了。</p>
<p>这时候，我就需要调用 Neutron 的 API，把这个 Key 对应的 Neutron 网络从真实的集群里删除掉。</p>
<p><strong>而如果能够获取到对应的 Network 对象，我就可以执行控制器模式里的对比“期望状态”和“实际状态”的逻辑了。</strong></p>
<p>其中，自定义控制器“千辛万苦”拿到的这个 Network 对象，<strong>正是 APIServer 里保存的“期望状态”</strong>，即：用户通过 YAML 文件提交到 APIServer 里的信息。当然，在我们的例子里，它已经被 Informer 缓存在了本地。</p>
<p><strong>那么，“实际状态”又从哪里来呢？</strong></p>
<p>当然是来自于实际的集群了。</p>
<p>所以，我们的控制循环需要通过 Neutron API 来查询实际的网络情况。</p>
<p>比如，我可以先通过 Neutron 来查询这个 Network 对象对应的真实网络是否存在。</p>
<ul>
<li>如果不存在，这就是一个典型的“期望状态”与“实际状态”不一致的情形。这时，我就需要使用这个 Network 对象里的信息（比如：CIDR 和 Gateway），调用 Neutron API 来创建真实的网络。</li>
<li>如果存在，那么，我就要读取这个真实网络的信息，判断它是否跟 Network 对象里的信息一致，从而决定我是否要通过 Neutron 来更新这个已经存在的真实网络。</li>
</ul>
<p>这样，我就通过对比“期望状态”和“实际状态”的差异，完成了一次调协（Reconcile）的过程。</p>
<p>至此，一个完整的自定义 API 对象和它所对应的自定义控制器，就编写完毕了。</p>
<blockquote>
<p>备注：与 Neutron 相关的业务代码并不是本篇文章的重点，所以我仅仅通过注释里的伪代码为你表述了这部分内容。如果你对这些代码感兴趣的话，可以自行完成。最简单的情况，你可以自己编写一个 Neutron Mock，然后输出对应的操作日志。</p>
</blockquote>
<p>接下来，我们就一起来把这个项目运行起来，查看一下它的工作情况。</p>
<p>你可以自己编译这个项目，也可以直接使用我编译好的二进制文件（samplecrd-controller）。编译并启动这个项目的具体流程如下所示：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> Clone repo</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git <span class="built_in">clone</span> https://github.com/resouer/k8s-controller-custom-resource$ <span class="built_in">cd</span> k8s-controller-custom-resource</span></span><br><span class="line"> </span><br><span class="line"><span class="meta">#</span><span class="bash"><span class="comment">## Skip this part if you don't want to build</span></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Install dependency</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> go get github.com/tools/godep</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> godep restore</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Build</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> go build -o samplecrd-controller .</span></span><br><span class="line"> </span><br><span class="line"><span class="meta">$</span><span class="bash"> ./samplecrd-controller -kubeconfig=<span class="variable">$HOME</span>/.kube/config -alsologtostderr=<span class="literal">true</span></span></span><br><span class="line">I0915 12:50:29.051349   27159 controller.go:84] Setting up event handlers</span><br><span class="line">I0915 12:50:29.051615   27159 controller.go:113] Starting Network control loop</span><br><span class="line">I0915 12:50:29.051630   27159 controller.go:116] Waiting for informer caches to sync</span><br><span class="line">E0915 12:50:29.066745   27159 reflector.go:134] github.com/resouer/k8s-controller-custom-resource/pkg/client/informers/externalversions/factory.go:117: Failed to list *v1.Network: the server could not find the requested resource (get networks.samplecrd.k8s.io)</span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p>你可以看到，自定义控制器被启动后，一开始会报错。</p>
<p>这是因为，此时 Network 对象的 CRD 还没有被创建出来，所以 Informer 去 APIServer 里“获取”（List）Network 对象时，并不能找到 Network 这个 API 资源类型的定义，即：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Failed to list *v1.Network: the server could not find the requested resource (get networks.samplecrd.k8s.io)</span><br></pre></td></tr></table></figure>
<p>所以，接下来我就需要创建 Network 对象的 CRD，这个操作在上一篇文章里已经介绍过了。</p>
<p>在另一个 shell 窗口里执行：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> kubectl apply -f crd/network.yaml</span></span><br></pre></td></tr></table></figure>
<p>这时候，你就会看到控制器的日志恢复了正常，控制循环启动成功：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">I0915 12:50:29.051630   27159 controller.go:116] Waiting for informer caches to sync</span><br><span class="line">...</span><br><span class="line">I0915 12:52:54.346854   25245 controller.go:121] Starting workers</span><br><span class="line">I0915 12:52:54.346914   25245 controller.go:127] Started workers</span><br></pre></td></tr></table></figure>
<p>接下来，我就可以进行 Network 对象的增删改查操作了。</p>
<p>首先，创建一个 Network 对象：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> cat example/example-network.yaml </span></span><br><span class="line">apiVersion: samplecrd.k8s.io/v1</span><br><span class="line">kind: Network</span><br><span class="line">metadata:</span><br><span class="line">  name: example-network</span><br><span class="line">spec:</span><br><span class="line">  cidr: "192.168.0.0/16"</span><br><span class="line">  gateway: "192.168.0.1"</span><br><span class="line">  </span><br><span class="line"><span class="meta">$</span><span class="bash"> kubectl apply -f example/example-network.yaml </span></span><br><span class="line">network.samplecrd.k8s.io/example-network created</span><br></pre></td></tr></table></figure>
<p>这时候，查看一下控制器的输出：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">I0915 12:50:29.051349   27159 controller.go:84] Setting up event handlers</span><br><span class="line">I0915 12:50:29.051615   27159 controller.go:113] Starting Network control loop</span><br><span class="line">I0915 12:50:29.051630   27159 controller.go:116] Waiting for informer caches to sync</span><br><span class="line">...</span><br><span class="line">I0915 12:52:54.346854   25245 controller.go:121] Starting workers</span><br><span class="line">I0915 12:52:54.346914   25245 controller.go:127] Started workers</span><br><span class="line">I0915 12:53:18.064409   25245 controller.go:229] [Neutron] Try to process network: &amp;v1.Network&#123;TypeMeta:v1.TypeMeta&#123;Kind:&quot;&quot;, APIVersion:&quot;&quot;&#125;, ObjectMeta:v1.ObjectMeta&#123;Name:&quot;example-network&quot;, GenerateName:&quot;&quot;, Namespace:&quot;default&quot;, ... ResourceVersion:&quot;479015&quot;, ... Spec:v1.NetworkSpec&#123;Cidr:&quot;192.168.0.0/16&quot;, Gateway:&quot;192.168.0.1&quot;&#125;&#125; ...</span><br><span class="line">I0915 12:53:18.064650   25245 controller.go:183] Successfully synced &apos;default/example-network&apos;</span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p>可以看到，我们上面创建 example-network 的操作，触发了 EventHandler 的“添加”事件，从而被放进了工作队列。</p>
<p>紧接着，控制循环就从队列里拿到了这个对象，并且打印出了正在“处理”这个 Network 对象的日志。</p>
<p>可以看到，这个 Network 的 ResourceVersion，也就是 API 对象的版本号，是 479015，而它的 Spec 字段的内容，跟我提交的 YAML 文件一摸一样，比如，它的 CIDR 网段是：192.168.0.0/16。</p>
<p>这时候，我来修改一下这个 YAML 文件的内容，如下所示：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> cat example/example-network.yaml </span></span><br><span class="line">apiVersion: samplecrd.k8s.io/v1</span><br><span class="line">kind: Network</span><br><span class="line">metadata:</span><br><span class="line">  name: example-network</span><br><span class="line">spec:</span><br><span class="line">  cidr: "192.168.1.0/16"</span><br><span class="line">  gateway: "192.168.1.1"</span><br></pre></td></tr></table></figure>
<p>可以看到，我把这个 YAML 文件里的 CIDR 和 Gateway 字段的修改成了 192.168.1.0/16 网段。</p>
<p>然后，我们执行了 kubectl apply 命令来提交这次更新，如下所示：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> kubectl apply -f example/example-network.yaml </span></span><br><span class="line">network.samplecrd.k8s.io/example-network configured</span><br></pre></td></tr></table></figure>
<p>这时候，我们就可以观察一下控制器的输出：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">I0915 12:53:51.126029   25245 controller.go:229] [Neutron] Try to process network: &amp;v1.Network&#123;TypeMeta:v1.TypeMeta&#123;Kind:&quot;&quot;, APIVersion:&quot;&quot;&#125;, ObjectMeta:v1.ObjectMeta&#123;Name:&quot;example-network&quot;, GenerateName:&quot;&quot;, Namespace:&quot;default&quot;, ...  ResourceVersion:&quot;479062&quot;, ... Spec:v1.NetworkSpec&#123;Cidr:&quot;192.168.1.0/16&quot;, Gateway:&quot;192.168.1.1&quot;&#125;&#125; ...</span><br><span class="line">I0915 12:53:51.126348   25245 controller.go:183] Successfully synced &apos;default/example-network&apos;</span><br></pre></td></tr></table></figure>
<p>可以看到，这一次，Informer 注册的“更新”事件被触发，更新后的 Network 对象的 Key 被添加到了工作队列之中。</p>
<p>所以，接下来控制循环从工作队列里拿到的 Network 对象，与前一个对象是不同的：它的 ResourceVersion 的值变成了 479062；而 Spec 里的字段，则变成了 192.168.1.0/16 网段。</p>
<p>最后，我再把这个对象删除掉：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> kubectl delete -f example/example-network.yaml</span></span><br></pre></td></tr></table></figure>
<p>这一次，在控制器的输出里，我们就可以看到，Informer 注册的“删除”事件被触发，并且控制循环“调用”Neutron API“删除”了真实环境里的网络。这个输出如下所示：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">W0915 12:54:09.738464   25245 controller.go:212] Network: default/example-network does not exist in local cache, will delete it from Neutron ...</span><br><span class="line">I0915 12:54:09.738832   25245 controller.go:215] [Neutron] Deleting network: default/example-network ...</span><br><span class="line">I0915 12:54:09.738854   25245 controller.go:183] Successfully synced &apos;default/example-network&apos;</span><br></pre></td></tr></table></figure>
<p>以上，就是编写和使用自定义控制器的全部流程了。</p>
<p>实际上，这套流程不仅可以用在自定义 API 资源上，也完全可以用在 Kubernetes 原生的默认 API 对象上。</p>
<p>比如，我们在 main 函数里，除了创建一个 Network Informer 外，还可以初始化一个 Kubernetes 默认 API 对象的 Informer 工厂，比如 Deployment 对象的 Informer。这个具体做法如下所示：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">  ...</span><br><span class="line">  </span><br><span class="line">  kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*<span class="number">30</span>)</span><br><span class="line">  </span><br><span class="line">  controller := NewController(kubeClient, exampleClient,</span><br><span class="line">  kubeInformerFactory.Apps().V1().Deployments(),</span><br><span class="line">  networkInformerFactory.Samplecrd().V1().Networks())</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">go</span> kubeInformerFactory.Start(stopCh)</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在这段代码中，我们<strong>首先</strong>使用 Kubernetes 的 client（kubeClient）创建了一个工厂；</p>
<p><strong>然后</strong>，我用跟 Network 类似的处理方法，生成了一个 Deployment Informer；</p>
<p><strong>接着</strong>，我把 Deployment Informer 传递给了自定义控制器；当然，我也要调用 Start 方法来启动这个 Deployment Informer。</p>
<p>而有了这个 Deployment Informer 后，这个控制器也就持有了所有 Deployment 对象的信息。接下来，它既可以通过 deploymentInformer.Lister() 来获取 Etcd 里的所有 Deployment 对象，也可以为这个 Deployment Informer 注册具体的 Handler 来。</p>
<p>更重要的是，<strong>这就使得在这个自定义控制器里面，我可以通过对自定义 API 对象和默认 API 对象进行协同，从而实现更加复杂的编排功能</strong>。</p>
<p>比如：用户每创建一个新的 Deployment，这个自定义控制器，就可以为它创建一个对应的 Network 供它使用。</p>
<h2 id="例子2"><a href="#例子2" class="headerlink" title="例子2"></a>例子2</h2><p>控制器主要使用以下client-go组件：</p>
<ol>
<li>Informer/SharedInformer：监控目标K8S资源的变化，并交由ResourceEventHandler处理</li>
<li>ResourceEventHandler：通常是将事件发送到工作队列</li>
<li>Workqueue ：暂存资源变更事件，由控制循环取出事件并处理</li>
</ol>
<h3 id="Informer"><a href="#Informer" class="headerlink" title="Informer"></a><strong>Informer</strong></h3><p>此组件负责获取对象状态，通常你不会直接向API Server发请求，而是通过client-go提供的编程接口。client-go提供了缓存功能，避免反复从API Server获取数据。</p>
<p>如果仅仅需要关注对象的创建、修改、删除事件，可以使用ListerWatcher接口。该接口可以对特定的资源进行监控（watch）操作：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">"k8s.io/client-go/tools/cache"</span></span><br><span class="line"><span class="comment">// 返回ListWatch结构，它实现了ListerWatcher接口</span></span><br><span class="line">lw := cache.NewListWatchFromClient(</span><br><span class="line">      client,   <span class="comment">// 客户端</span></span><br><span class="line">      &amp;v1.Pod&#123;&#125;, <span class="comment">// 被监控资源类型</span></span><br><span class="line">      api.NamespaceAll, <span class="comment">// 被监控命名空间</span></span><br><span class="line">      fieldSelector) <span class="comment">// 选择器，减少匹配的资源数量</span></span><br></pre></td></tr></table></figure>
<p>有了ListerWatcher你就可以创建Informer了：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">store, controller := cache.NewInformer (</span><br><span class="line">    &amp;lw,</span><br><span class="line">    &amp;v1.Pod&#123;&#125;,           <span class="comment">// 监控的对象类型</span></span><br><span class="line">    resyncPeriod,        <span class="comment">// 如果非0则自动定期relist对象</span></span><br><span class="line">    cache.ResourceEventHandlerFuncs&#123;&#125; <span class="comment">// ResourceEventHandler 事件发送给此对象处理</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p>实际编程时并不常使用Informer，下文会提到的SharedInformer使用的更多。</p>
<h3 id="ListWatcher"><a href="#ListWatcher" class="headerlink" title="ListWatcher"></a><strong>ListWatcher</strong></h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ListerWatcher是任何支持对一个资源进行init list，并进行watch的对象</span></span><br><span class="line"><span class="keyword">type</span> ListerWatcher <span class="keyword">interface</span> &#123;</span><br><span class="line">    List(options metav1.ListOptions) (runtime.Object, error)</span><br><span class="line">        <span class="comment">// watch能保证对资源进行持续不断的监控</span></span><br><span class="line">    Watch(options metav1.ListOptions) (watch.Interface, error)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>上文调用的cache.NewListWatchFromClient，已经提供了ListWatcher的实现：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewFilteredListWatchFromClient</span><span class="params">(c Getter, resource <span class="keyword">string</span>, namespace <span class="keyword">string</span>, optionsModifier <span class="keyword">func</span>(options *metav1.ListOptions)</span>) *<span class="title">ListWatch</span></span> &#123;</span><br><span class="line">    <span class="comment">// ListWatch已经实现List方法，并代理给其成员函数listFunc，Watch方法类似</span></span><br><span class="line"> </span><br><span class="line">        listFunc := <span class="function"><span class="keyword">func</span><span class="params">(options metav1.ListOptions)</span> <span class="params">(runtime.Object, error)</span></span> &#123;</span><br><span class="line">        optionsModifier(&amp;options) <span class="comment">// 修改选项的回调</span></span><br><span class="line">        <span class="keyword">return</span> c.Get(). <span class="comment">// 获得*restclient.Request，此结构允许你以链式调用方式构建对API的请求</span></span><br><span class="line">            Namespace(namespace). <span class="comment">// 限定命名空间</span></span><br><span class="line">            Resource(resource). <span class="comment">// 限定资源类型</span></span><br><span class="line">            VersionedParams(&amp;options, metav1.ParameterCodec). <span class="comment">// 解析并限定资源版本</span></span><br><span class="line">            Do(). <span class="comment">// 执行请求并获得Result</span></span><br><span class="line">            Get() <span class="comment">// 获取Result中的runtime.Object对象</span></span><br><span class="line">    &#125;</span><br><span class="line">    watchFunc := <span class="function"><span class="keyword">func</span><span class="params">(options metav1.ListOptions)</span> <span class="params">(watch.Interface, error)</span></span> &#123;</span><br><span class="line">        options.Watch = <span class="literal">true</span></span><br><span class="line">        optionsModifier(&amp;options)</span><br><span class="line">        <span class="keyword">return</span> c.Get().</span><br><span class="line">            Namespace(namespace).</span><br><span class="line">            Resource(resource).</span><br><span class="line">            VersionedParams(&amp;options, metav1.ParameterCodec).</span><br><span class="line">            Watch() <span class="comment">// 尝试对请求的API进行监控，返回watch.Interface</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> &amp;ListWatch&#123;ListFunc: listFunc, WatchFunc: watchFunc&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="ResourceEventHandler"><a href="#ResourceEventHandler" class="headerlink" title="ResourceEventHandler"></a>ResourceEventHandler</h3><p>通常在此接口中提供事件处理逻辑</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> ResourceEventHandler <span class="keyword">interface</span> &#123;</span><br><span class="line">        <span class="comment">// 当资源第一次加入到Informer的缓存后调用</span></span><br><span class="line">    OnAdd(obj <span class="keyword">interface</span>&#123;&#125;)</span><br><span class="line">        <span class="comment">// 当既有资源被修改时调用。oldObj是资源的上一个状态，newObj则是新状态</span></span><br><span class="line">        <span class="comment">// resync时此方法也被调用，即使对象没有任何变化</span></span><br><span class="line">    OnUpdate(oldObj, newObj <span class="keyword">interface</span>&#123;&#125;)</span><br><span class="line">        <span class="comment">// 当既有资源被删除时调用，obj是对象的最后状态，如果最后状态未知则返回DeletedFinalStateUnknown</span></span><br><span class="line">    OnDelete(obj <span class="keyword">interface</span>&#123;&#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="ResyncPeriod"><a href="#ResyncPeriod" class="headerlink" title="ResyncPeriod"></a>ResyncPeriod</h3><p>规定每隔多久，控制器遍历缓存中所有对象，并调用OnUpdate。</p>
<p>如果控制器可能错过对象更新事件，或者先前的事件处理回调可能执行失败，则此配置参数很重要。</p>
<h3 id="SharedInformer"><a href="#SharedInformer" class="headerlink" title="SharedInformer"></a>SharedInformer</h3><p>Informer会创建一个私有的缓存，其中包含它自己用到的所有资源。但是，在K8S中有很多控制器在运行，它们关注多种类型的对象。如果基于Informer实现这些控制器，就会有很多重复的缓存数据，增加资源占用。</p>
<p>SharedInformer能够创建一个共享的缓存，在多个控制器之间共享数据。此外，不管下游有多少个消费者，SharedInformer都仅仅对上游服务器建立一个Watch。因此SharedInformer同时降低了客户端的内存占用和服务器的负载。包含很多控制器的 kube-controller-manager使用SharedInformer。</p>
<p>SharedInformer直接提供了接受新增、更新、删除特定资源的钩子。</p>
<p>类似于Informer，cache模块也为SharedInformer提供了工厂函数：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewSharedInformer</span><span class="params">(lw ListerWatcher, objType runtime.Object, resyncPeriod time.Duration)</span> <span class="title">SharedInformer</span></span> &#123;</span><br><span class="line">    <span class="keyword">return</span> NewSharedIndexInformer(lw, objType, resyncPeriod, Indexers&#123;&#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="Workqueue"><a href="#Workqueue" class="headerlink" title="Workqueue"></a>Workqueue</h3><p>由于SharedInformer是共享的，因此它不能跟踪每个控制器处理事件的进度。控制器必须提供自己的队列和重试（处理）机制。</p>
<p>当资源状态变化后，SharedInformer的ResourceEventHandler在Workqueue中添加一个Key。Key的格式是 资源命名空间/资源名称，资源命名空间是可以省略的。</p>
<p>client-go/util/workqueue.提供了多种工作队列的实现，包括：</p>
<ol>
<li>延迟队列，延后一段时间再将元素入队，由接口DelayingInterface提供</li>
<li>限速队列，限定单位时间内能够入队的元素量，由接口RateLimitingInterface提供</li>
</ol>
<p>下面的代码示意了如何创建限速队列：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())</span><br></pre></td></tr></table></figure>
<p>一个Key在工作队列中的生命周期如下：</p>
<ol>
<li>queue.Add(key)入队</li>
<li>queue.Get()获取第一个Key进行处理，如果：<ol>
<li>处理成功，queue.Forget(key)清除掉Key</li>
<li>处理失败，在到达最大重试次数之前，控制器调用queue.AddRateLimited(key)重新入队</li>
</ol>
</li>
<li>queue.Forget(key)仅仅让队列不再跟踪事件的历史。控制器会最终调用queue.Done()彻底删除事件</li>
</ol>
<p>控制器仅仅（如果自己实现，也应该遵守此准则）在缓存完整同步后，才调用Worker，处理Workqueue，原因是：</p>
<ol>
<li>直到缓存同步完毕，列出的资源才是精确的</li>
<li>可以让针对单个资源的多次更新合并为一个，避免反复处理中间状态，浪费资源</li>
</ol>
<p>一个简单控制器的例子</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"> </span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">    <span class="string">"flag"</span></span><br><span class="line">    <span class="string">"k8s.io/client-go/kubernetes"</span></span><br><span class="line">    <span class="string">"k8s.io/client-go/util/workqueue"</span></span><br><span class="line">    <span class="string">"k8s.io/sample-controller/pkg/signals"</span></span><br><span class="line">    <span class="string">"k8s.io/client-go/tools/cache"</span></span><br><span class="line">    <span class="string">"k8s.io/client-go/tools/clientcmd"</span></span><br><span class="line">    <span class="string">"github.com/golang/glog"</span></span><br><span class="line">    <span class="string">"k8s.io/apimachinery/pkg/watch"</span></span><br><span class="line">    metav1 <span class="string">"k8s.io/apimachinery/pkg/apis/meta/v1"</span></span><br><span class="line">    <span class="string">"k8s.io/apimachinery/pkg/runtime"</span></span><br><span class="line">    utilruntime <span class="string">"k8s.io/apimachinery/pkg/util/runtime"</span></span><br><span class="line">    apiv1 <span class="string">"k8s.io/api/core/v1"</span></span><br><span class="line">    <span class="string">"fmt"</span></span><br><span class="line">    <span class="string">"k8s.io/apimachinery/pkg/util/wait"</span></span><br><span class="line">    <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"> </span><br><span class="line"><span class="comment">/* 控制器 */</span></span><br><span class="line"><span class="keyword">type</span> Controller <span class="keyword">struct</span> &#123;</span><br><span class="line">    <span class="comment">// 此控制器使用的客户端</span></span><br><span class="line">    clientset kubernetes.Interface</span><br><span class="line">    <span class="comment">// 此控制器使用的工作队列</span></span><br><span class="line">    queue workqueue.RateLimitingInterface</span><br><span class="line">    <span class="comment">// 此控制器使用的共享Informer，SharedIndexInformer可以维护缓存中对象的索引</span></span><br><span class="line">    informer cache.SharedIndexInformer</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="comment">/* 主函数 */</span></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">    <span class="comment">// 参数变量</span></span><br><span class="line">    masterURL  <span class="keyword">string</span></span><br><span class="line">    kubeconfig <span class="keyword">string</span></span><br><span class="line">)</span><br><span class="line"><span class="comment">// 启动控制器</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Controller)</span> <span class="title">Run</span><span class="params">(stopCh &lt;-<span class="keyword">chan</span> <span class="keyword">struct</span>&#123;&#125;)</span></span> &#123;</span><br><span class="line">    <span class="comment">// 捕获应用程序崩溃并打印日志</span></span><br><span class="line">    <span class="keyword">defer</span> utilruntime.HandleCrash()</span><br><span class="line">    <span class="comment">// 关闭队列，从而导致Worker结束</span></span><br><span class="line">    <span class="keyword">defer</span> c.queue.ShutDown()</span><br><span class="line"> </span><br><span class="line">    glog.Info(<span class="string">"启动控制器……"</span>)</span><br><span class="line"> </span><br><span class="line">    <span class="comment">// 运行Informer</span></span><br><span class="line">    <span class="keyword">go</span> c.informer.Run(stopCh)</span><br><span class="line"> </span><br><span class="line">    <span class="comment">// 在启动Worker之前，等待缓存同步完成</span></span><br><span class="line">    <span class="keyword">if</span> !cache.WaitForCacheSync(stopCh, c.informer.HasSynced) &#123;</span><br><span class="line">        utilruntime.HandleError(fmt.Errorf(<span class="string">"同步缓存超时"</span>))</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    glog.Info(<span class="string">"缓存已经同步，准备启动Worker"</span>)</span><br><span class="line">    <span class="comment">// 循环执行Worker，直到TERM</span></span><br><span class="line">    wait.Until(c.runWorker, time.Second, stopCh)</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="comment">// 启动Worker</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Controller)</span> <span class="title">runWorker</span><span class="params">()</span></span> &#123;</span><br><span class="line">    <span class="keyword">for</span> c.processNextItem() &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="comment">// Worker的逻辑框架</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Controller)</span> <span class="title">processNextItem</span><span class="params">()</span> <span class="title">bool</span></span> &#123;</span><br><span class="line">    <span class="comment">// 最大重试次数</span></span><br><span class="line">    maxRetries := <span class="number">3</span></span><br><span class="line"> </span><br><span class="line">    <span class="comment">// 获取下一个元素，第2个出参提示队列是否已经关闭</span></span><br><span class="line">    key, quit := c.queue.Get()</span><br><span class="line">    <span class="keyword">if</span> quit &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="comment">// 总是移除Key</span></span><br><span class="line">    <span class="keyword">defer</span> c.queue.Done(key)</span><br><span class="line"> </span><br><span class="line">    <span class="comment">// 处理Key</span></span><br><span class="line">    err := c.processItem(key.(<span class="keyword">string</span>))</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">if</span> err == <span class="literal">nil</span> &#123;</span><br><span class="line">        <span class="comment">// 处理成功，提示队列不再跟踪事件历史</span></span><br><span class="line">        c.queue.Forget(key)</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> c.queue.NumRequeues(key) &lt; maxRetries &#123;</span><br><span class="line">        glog.Errorf(<span class="string">"处理%s事件失败，准备重试： %v"</span>, key, err)</span><br><span class="line">        c.queue.AddRateLimited(key)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        glog.Errorf(<span class="string">"处理%s事件失败，放弃： %v"</span>, key, err)</span><br><span class="line">        c.queue.Forget(key)</span><br><span class="line">        utilruntime.HandleError(err)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="comment">// Worker核心逻辑</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Controller)</span> <span class="title">processItem</span><span class="params">(key <span class="keyword">string</span>)</span> <span class="title">error</span></span> &#123;</span><br><span class="line">    glog.Infof(<span class="string">"开始处理事件%s"</span>, key)</span><br><span class="line">    <span class="comment">// 根据Key获取对象</span></span><br><span class="line">    obj, exists, err := c.informer.GetIndexer().GetByKey(key)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> fmt.Errorf(<span class="string">"获取对象%s失败: %v"</span>, key, err)</span><br><span class="line">    &#125;</span><br><span class="line">    fmt.Print(obj)</span><br><span class="line">    <span class="keyword">if</span> !exists &#123;</span><br><span class="line">        <span class="comment">// 在这里处理对象删除事件</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 在这里处理对象创建事件</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 因为不进行Resync，不会有更新事件</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    <span class="comment">// 解析参数，存入上述变量</span></span><br><span class="line">    flag.Parse()</span><br><span class="line">    cfg, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        glog.Fatalf(<span class="string">"构建kubeconfig失败: %s"</span>, err.Error())</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 创建客户端，Clientset是一系列K8S API的集合</span></span><br><span class="line">    clientset, err := kubernetes.NewForConfig(cfg)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        glog.Fatalf(<span class="string">"构建clientset失败: %s"</span>, err.Error())</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 信号处理通道，当进程接收到信号后，此通道可读</span></span><br><span class="line">    stopCh := signals.SetupSignalHandler()</span><br><span class="line"> </span><br><span class="line">    queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())</span><br><span class="line"> </span><br><span class="line">    informer := cache.NewSharedIndexInformer(</span><br><span class="line">        &amp;cache.ListWatch&#123;</span><br><span class="line">            ListFunc: <span class="function"><span class="keyword">func</span><span class="params">(options metav1.ListOptions)</span> <span class="params">(runtime.Object, error)</span></span> &#123;</span><br><span class="line">                <span class="comment">// 仅仅列出所有命名空间的Pod</span></span><br><span class="line">                <span class="keyword">return</span> clientset.CoreV1().Pods(metav1.NamespaceAll).List(options)</span><br><span class="line">            &#125;,</span><br><span class="line">            WatchFunc: <span class="function"><span class="keyword">func</span><span class="params">(options metav1.ListOptions)</span> <span class="params">(watch.Interface, error)</span></span> &#123;</span><br><span class="line">                <span class="keyword">return</span> clientset.CoreV1().Pods(metav1.NamespaceAll).Watch(options)</span><br><span class="line">            &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">        &amp;apiv1.Pod&#123;&#125;,</span><br><span class="line">        <span class="number">0</span>,                <span class="comment">// 不进行relist</span></span><br><span class="line">        cache.Indexers&#123;&#125;, <span class="comment">// map[string]IndexFunc</span></span><br><span class="line">    )</span><br><span class="line"> </span><br><span class="line">    <span class="comment">// 添加事件处理回调，仅仅是简单的入队</span></span><br><span class="line">    informer.AddEventHandler(cache.ResourceEventHandlerFuncs&#123;<span class="comment">// 此结构实现ResourceEventHandler</span></span><br><span class="line">        AddFunc: <span class="function"><span class="keyword">func</span><span class="params">(obj <span class="keyword">interface</span>&#123;&#125;)</span></span> &#123;</span><br><span class="line">            <span class="comment">// 从对象中抽取Key</span></span><br><span class="line">            key, err := cache.MetaNamespaceKeyFunc(obj)</span><br><span class="line">            <span class="keyword">if</span> err == <span class="literal">nil</span> &#123;</span><br><span class="line">                queue.Add(key)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        DeleteFunc: <span class="function"><span class="keyword">func</span><span class="params">(obj <span class="keyword">interface</span>&#123;&#125;)</span></span> &#123;</span><br><span class="line">            key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)</span><br><span class="line">            <span class="keyword">if</span> err == <span class="literal">nil</span> &#123;</span><br><span class="line">                queue.Add(key)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;)</span><br><span class="line"> </span><br><span class="line">    <span class="comment">// 构建控制器对象</span></span><br><span class="line">    ctrl := Controller&#123;</span><br><span class="line">        clientset,</span><br><span class="line">        queue,</span><br><span class="line">        informer,</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="comment">// 启动</span></span><br><span class="line">    ctrl.Run(stopCh)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>获取对象内容</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">newDepl = <span class="built_in">new</span>.(*appsv1.Deployment)</span><br></pre></td></tr></table></figure>
<p>判断对象是否有变化</p>
<p>利用ObjectMeta.ResourceVersion，资源有变化后此字段即改变</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">deploymentInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs&#123;</span><br><span class="line">        <span class="comment">// 资源添加到缓存中后调用（不是在集群中创建后）</span></span><br><span class="line">    AddFunc: controller.handleObject,</span><br><span class="line">        <span class="comment">// 既有资源修改后调用。当执行resync后，该回调也被调用，即使资源没有任何变化</span></span><br><span class="line">    UpdateFunc: <span class="function"><span class="keyword">func</span><span class="params">(old, <span class="built_in">new</span> <span class="keyword">interface</span>&#123;&#125;)</span></span> &#123;</span><br><span class="line">        newDepl := <span class="built_in">new</span>.(*appsv1.Deployment)</span><br><span class="line">        oldDepl := old.(*appsv1.Deployment)</span><br><span class="line">        <span class="keyword">if</span> newDepl.ResourceVersion == oldDepl.ResourceVersion &#123;</span><br><span class="line">            <span class="comment">// 如果资源没有变化则直接返回</span></span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        &#125;</span><br><span class="line">        controller.handleObject(<span class="built_in">new</span>)</span><br><span class="line">    &#125;,</span><br><span class="line">        <span class="comment">// 当既有资源被删除后调用，入参是资源的最终状态</span></span><br><span class="line">        <span class="comment">// 如果最终状态未知，则入参是DeletedFinalStateUnknown。这种情况的原因可能是watch关闭而错过</span></span><br><span class="line">        <span class="comment">// 资源的删除事件，直到下次relist控制器才意识到资源被删除</span></span><br><span class="line">    DeleteFunc: controller.handleObject,</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>###代码生成</p>
<p><a href="https://github.com/kubernetes/code-generator" target="_blank" rel="noopener">code-generator</a>是K8S提供的一个代码生成器项目，可以用来：</p>
<ol>
<li>开发CRD的控制器时，生成版本化的、类型化的客户端代码（clientset），以及Lister、Informer代码</li>
<li>开发API聚合时，在内部和版本化的类型、defaulters、protobuf编解码器、client、informer之间进行转换</li>
</ol>
<p>K8S本身以及OpenShift也在使用此项目。</p>
<p>code-generator提供的，和CRD有关的生成器包括：</p>
<ol>
<li>deepcopy-gen：为每个T类型生成 func (t<em> T) DeepCopy() </em>T方法。API类型都需要实现深拷贝</li>
<li>client-gen：为CustomResource API组生成强类型的clientset</li>
<li>informer-gen：为CustomResources生成Informer</li>
<li>lister-gen：为CustomResources生成Lister，Lister为GET/LIST请求提供只读缓存层</li>
</ol>
<p>Informer和Lister是构建控制器（或者叫Operetor）的基本要素。使用这4个代码生成器可以创建全功能的、和K8S上游控制器工作机制相同的production-ready的控制器。</p>
<p>code-generator还包含一些其它的代码生成器。例如Conversion-gen负责产生内外部类型的转换函数、Defaulter-gen负责处理字段默认值。</p>
<p><a href="https://github.com/openshift-evangelists/crd-code-generation" target="_blank" rel="noopener">crd-code-generation</a>是使用代码生成器的一个示例项目，可以作为你的实际项目的起点</p>
<p>####调用代码生成器</p>
<p><a href="https://github.com/kubernetes/code-generator" target="_blank" rel="noopener">code-generator</a>基于<a href="https://github.com/kubernetes/gengo" target="_blank" rel="noopener">k8s.io/gengo</a>实现，两者共享一部分命令行标记。大部分的生成器支持–input-dirs参数来读取一系列输入包，处理其中的每个类型，然后生成代码：</p>
<ol>
<li>部分代码生成到输入包所在目录，例如deepcopy-gen生成器。可以使用参数 –output-file-base “zz_generated.deepcopy”来定义输出文件名</li>
<li>其它代码生成到–output-package指定的目录（通常为pkg/client），例如client-gen、informer-gem、lister-gen等生成器</li>
</ol>
<p>开发CRD时，你可以使用generator-group.sh脚本而不是逐个手工调用生成器。通常可以在项目中编写hack/update-codegen.sh：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"> </span><br><span class="line"><span class="built_in">set</span> -o errexit</span><br><span class="line"><span class="built_in">set</span> -o nounset</span><br><span class="line"><span class="built_in">set</span> -o pipefail</span><br><span class="line"> </span><br><span class="line">SCRIPT_ROOT=$(dirname <span class="variable">$&#123;BASH_SOURCE&#125;</span>)/..</span><br><span class="line"><span class="comment"># 代码生成器包位置</span></span><br><span class="line">CODEGEN_PKG=<span class="variable">$&#123;CODEGEN_PKG:-$(cd $&#123;SCRIPT_ROOT&#125;</span>; ls -d -1 ./vendor/k8s.io/code-generator 2&gt;/dev/null || <span class="built_in">echo</span> <span class="variable">$&#123;GOPATH&#125;</span>/src/k8s.io/code-generator)&#125;</span><br><span class="line"> </span><br><span class="line"><span class="comment"># generate-groups.sh &lt;generators&gt; &lt;output-package&gt; &lt;apis-package&gt; &lt;groups-versions&gt;</span></span><br><span class="line"><span class="comment">#                    使用哪些生成器，可选值deepcopy,defaulter,client,lister,informer，逗号分隔，all表示全部使用</span></span><br><span class="line"><span class="comment">#                                 输出包的导入路径</span></span><br><span class="line"><span class="comment">#                                                  CR定义所在路径</span></span><br><span class="line"><span class="comment">#                                                                 API组和版本</span></span><br><span class="line">vendor/k8s.io/code-generator/generate-groups.sh all \</span><br><span class="line">  github.com/openshift-evangelists/crd-code-generation/pkg/client \</span><br><span class="line">  github.com/openshift-evangelists/crd-code-generation/pkg/apis \</span><br><span class="line">  example.com:v1 \</span><br><span class="line">  <span class="comment"># 自动生成的源码，头部附加的内容</span></span><br><span class="line">  --go-header-file <span class="variable">$&#123;SCRIPT_ROOT&#125;</span>/hack/custom-boilerplate.go.txt</span><br></pre></td></tr></table></figure>
<p>执行上面的脚本后，所有API代码会生成在pkg/apis目录下，clientsets、informers、listers则生成在pkg/client目录下。 </p>
<p>你可以进一步提供hack/update-codegen.sh脚本，用于判断生成的代码是否up-to-date：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash">!/bin/bash</span></span><br><span class="line"> </span><br><span class="line">set -o errexit</span><br><span class="line">set -o nounset</span><br><span class="line">set -o pipefail</span><br><span class="line"> </span><br><span class="line"><span class="meta">#</span><span class="bash"> 先调用update-codegen.sh生成一份新代码，然后对比新老代码是否一样</span></span><br><span class="line"> </span><br><span class="line">SCRIPT_ROOT=$(dirname "$&#123;BASH_SOURCE&#125;")/..</span><br><span class="line">DIFFROOT="$&#123;SCRIPT_ROOT&#125;/pkg"</span><br><span class="line">TMP_DIFFROOT="$&#123;SCRIPT_ROOT&#125;/_tmp/pkg"</span><br><span class="line">_tmp="$&#123;SCRIPT_ROOT&#125;/_tmp"</span><br><span class="line"> </span><br><span class="line">cleanup() &#123;</span><br><span class="line">    rm -rf "$&#123;_tmp&#125;"</span><br><span class="line">&#125;</span><br><span class="line">trap "cleanup" EXIT SIGINT</span><br><span class="line"> </span><br><span class="line">cleanup</span><br><span class="line"> </span><br><span class="line">mkdir -p "$&#123;TMP_DIFFROOT&#125;"</span><br><span class="line">cp -a "$&#123;DIFFROOT&#125;"/* "$&#123;TMP_DIFFROOT&#125;"</span><br><span class="line"> </span><br><span class="line">"$&#123;SCRIPT_ROOT&#125;/hack/update-codegen.sh"</span><br><span class="line">echo "diffing $&#123;DIFFROOT&#125; against freshly generated codegen"</span><br><span class="line">ret=0</span><br><span class="line">diff -Naupr "$&#123;DIFFROOT&#125;" "$&#123;TMP_DIFFROOT&#125;" || ret=$?</span><br><span class="line">cp -a "$&#123;TMP_DIFFROOT&#125;"/* "$&#123;DIFFROOT&#125;"</span><br><span class="line">if [[ $ret -eq 0 ]]</span><br><span class="line">then</span><br><span class="line">    echo "$&#123;DIFFROOT&#125; up to date."</span><br><span class="line">else</span><br><span class="line">    echo "$&#123;DIFFROOT&#125; is out of date. Please run hack/update-codegen.sh"</span><br><span class="line">    exit 1</span><br><span class="line">fi</span><br></pre></td></tr></table></figure>
<p>####控制代码生成</p>
<p>除了通过命令行标记控制代码生成器之外，你还可以在go源码中使用tag来设定一些供生成器使用的属性。这些tag分为两类：</p>
<ol>
<li>在doc.go的package语句之上提供的全局tag</li>
<li>在需要被处理的类型上提供的局部tag</li>
</ol>
<p>tag的语法如下：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// +tag-name</span></span><br><span class="line"><span class="comment">// 或者</span></span><br><span class="line"><span class="comment">// +tag-name=value</span></span><br></pre></td></tr></table></figure>
<p>也就是说，tag是以注释的形式存在的。tag的位置很重要，很多tag必须直接位于type或package语句的上一行，另外一些则必须和go语句隔开至少一行空白。</p>
<p><strong>全局tag</strong></p>
<p>必须在目标包的doc.go文件中声明，典型路径是 pkg/apis/<apigroup>/<version>/doc.go。 内容示例：</version></apigroup></p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 为包中任何类型生成深拷贝方法，可以在局部tag覆盖此默认行为</span></span><br><span class="line"><span class="comment">// register关键字现在已经不需要，它的含义是将深拷贝方法注册到scheme中，从1.9开始scheme不再负责runtime.Object的深拷贝</span></span><br><span class="line"><span class="comment">// 你只需要直接调用obj.DeepCopy/DeepCopyObject()即可</span></span><br><span class="line"><span class="comment">// +k8s:deepcopy-gen=package,register</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">// groupName指定API组的全限定名</span></span><br><span class="line"><span class="comment">// 此API组的v1版本，放在同一个包中</span></span><br><span class="line"><span class="comment">// +groupName=example.com</span></span><br><span class="line"><span class="keyword">package</span> v1</span><br></pre></td></tr></table></figure>
<p><strong>局部tag</strong></p>
<p>要么直接声明在类型之前，要么位于类型之前的第二个注释块中。下面的 types.go中声明了CR对应的go类型</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 为当前类型生成客户端</span></span><br><span class="line"><span class="comment">// +genclient</span></span><br><span class="line"><span class="comment">// 提示此类型不基于/status子资源来实现spec-status分离，产生的客户端不具有UpdateStatus方法</span></span><br><span class="line"><span class="comment">// 否则，只要类型具有Status字段，就会生成UpdateStatus方法</span></span><br><span class="line"><span class="comment">// +genclient:noStatus</span></span><br><span class="line"><span class="comment">// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">// K8S资源，数据库</span></span><br><span class="line"><span class="keyword">type</span> Database <span class="keyword">struct</span> &#123;</span><br><span class="line">    metav1.TypeMeta   <span class="string">`json:",inline"`</span></span><br><span class="line">    metav1.ObjectMeta <span class="string">`json:"metadata,omitempty"`</span></span><br><span class="line"> </span><br><span class="line">    Spec DatabaseSpec <span class="string">`json:"spec"`</span></span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="comment">// 数据库的Spec</span></span><br><span class="line"><span class="keyword">type</span> DatabaseSpec <span class="keyword">struct</span> &#123;</span><br><span class="line">    User     <span class="keyword">string</span> <span class="string">`json:"user"`</span></span><br><span class="line">    Password <span class="keyword">string</span> <span class="string">`json:"password"`</span></span><br><span class="line">    Encoding <span class="keyword">string</span> <span class="string">`json:"encoding,omitempty"`</span></span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="comment">// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">// K8S资源，数据库列表</span></span><br><span class="line"><span class="keyword">type</span> DatabaseList <span class="keyword">struct</span> &#123;</span><br><span class="line">    metav1.TypeMeta <span class="string">`json:",inline"`</span></span><br><span class="line">    metav1.ListMeta <span class="string">`json:"metadata"`</span></span><br><span class="line"> </span><br><span class="line">    Items []Database <span class="string">`json:"items"`</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>内嵌了metav1.TypeMeta通常都是顶级类型，实现runtime.Object，一般需要为它们生成client。</p>
<p>对于集群级别（非命名空间内）的资源，你需要提供</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// +genclient:nonNamespaced</span></span><br></pre></td></tr></table></figure>
<p>你还可以控制客户端提供哪些HTTP方法：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// +genclient:noVerbs</span></span><br><span class="line"><span class="comment">// +genclient:onlyVerbs=create,delete</span></span><br><span class="line"><span class="comment">// +genclient:skipVerbs=get,list,create,update,patch,delete,deleteCollection,watch</span></span><br><span class="line"><span class="comment">// 仅仅返回Status而非整个资源</span></span><br><span class="line"><span class="comment">// +genclient:method=Create,verb=create,result=k8s.io/apimachinery/pkg/apis/meta/v1.Status</span></span><br></pre></td></tr></table></figure>

    </article>
    <!-- license  -->
    
        <div class="license-wrapper">
            <p>原文作者：<a href="haojianxun.github.io">HAOJX</a>
            <p>原文链接：<a href="haojianxun.github.io/2018/11/10/kubernetes CRD下篇--编写自定义控制器/">haojianxun.github.io/2018/11/10/kubernetes CRD下篇--编写自定义控制器/</a>
            <p>发表日期：<a href="haojianxun.github.io/2018/11/10/kubernetes CRD下篇--编写自定义控制器/">November 10th 2018, 12:00:00 am</a>
            <p>更新日期：<a href="haojianxun.github.io/2018/11/10/kubernetes CRD下篇--编写自定义控制器/">November 21st 2018, 2:59:31 pm</a>
            <p>版权声明：本文采用<a rel="license" href="http://creativecommons.org/licenses/by-nc/4.0/">知识共享署名-非商业性使用 4.0 国际许可协议</a>进行许可</p>
        </div>
    
    <!-- paginator  -->
    <ul class="post-paginator">
        <li class="next">
            
                <div class="nextSlogan">Next Post</div>
                <a href= "/2018/11/15/基于canel的网络策略/" title= "基于canel的网络策略">
                    <div class="nextTitle">基于canel的网络策略</div>
                </a>
            
        </li>
        <li class="previous">
            
                <div class="prevSlogan">Previous Post</div>
                <a href= "/2018/11/10/kubernetes动态准入控制Initializers是什么/" title= "kubernetes动态准入控制Initializers是什么">
                    <div class="prevTitle">kubernetes动态准入控制Initializers是什么</div>
                </a>
            
        </li>
    </ul>
    <!-- 评论插件 -->
    <!-- 来必力City版安装代码 -->

<!-- City版安装代码已完成 -->
    
    
    <!-- partial('_partial/comment/changyan') -->
    <!--PC版-->


    
    
    <script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
    <script src='//unpkg.com/valine/dist/Valine.min.js'></script>
    <div id="comment"></div>
    <script>
    new Valine({
        el: '#comment' ,
        notify:false, 
        verify:false, 
        appId: "qX8HVjkGrEtM7e6o1RGfcE5K-gzGzoHsz",
        appKey: "q6vTiIUxgUePH7KN2r1bSk6W",
        placeholder: "ヾﾉ≧∀≦)o来啊，快活啊!  如果有技术问题 , 可以在评论中留下您的昵称和邮箱 , 以便于回复",
        path:window.location.pathname, 
        avatar:'mm' 
    });
    </script>


    <!-- 评论 -->
</main>
            <!-- profile -->
            
        </div>
        <footer class="footer footer-unloaded">
    <!-- social  -->
    
    <div class="social">
        
    
        
            
                <a href="mailto:1090468413@qq.com" class="iconfont-archer email" title=email ></a>
            
        
    
        
            
                <a href="//profile-summary-for-github.com/user/haojianxun" class="iconfont-archer github" target="_blank" title=github></a>
            
        
    
        
            
                <span class="iconfont-archer wechat" title=wechat>
                  
                  <img class="profile-qr" src="/assets/example_qr.png" />
                </span>
            
        
    
        
    
        
    
        
    
        
    
        
    
        
    
        
    
        
    
        
    
        
    
        
    
        
    
        
    
        
    
        
    
        
    
        
    

    </div>
    
    <!-- powered by Hexo  -->
    <div class="copyright">
        <span id="hexo-power">Powered by <a href="https://hexo.io/" target="_blank">Hexo</a></span><span class="iconfont-archer power">&#xe635;</span><span id="theme-info">theme <a href="https://github.com/fi3ework/hexo-theme-archer" target="_blank">Archer</a></span>
    </div>
    <!-- 不蒜子  -->
    
    <div class="busuanzi-container">
    
     
    <span id="busuanzi_container_site_pv">PV: <span id="busuanzi_value_site_pv"></span> :)</span>
    
    </div>
    
</footer>
    </div>
    <!-- toc -->
    
    <div class="toc-wrapper" style=
    







top:50vh;

    >
        <div class="toc-catalog">
            <span class="iconfont-archer catalog-icon">&#xe613;</span><span>CATALOG</span>
        </div>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#自定义控制器的工作流程"><span class="toc-number">1.</span> <span class="toc-text">自定义控制器的工作流程</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#例子1"><span class="toc-number">2.</span> <span class="toc-text">例子1</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#例子2"><span class="toc-number">3.</span> <span class="toc-text">例子2</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Informer"><span class="toc-number">3.1.</span> <span class="toc-text">Informer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ListWatcher"><span class="toc-number">3.2.</span> <span class="toc-text">ListWatcher</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ResourceEventHandler"><span class="toc-number">3.3.</span> <span class="toc-text">ResourceEventHandler</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ResyncPeriod"><span class="toc-number">3.4.</span> <span class="toc-text">ResyncPeriod</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#SharedInformer"><span class="toc-number">3.5.</span> <span class="toc-text">SharedInformer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Workqueue"><span class="toc-number">3.6.</span> <span class="toc-text">Workqueue</span></a></li></ol></li></ol>
    </div>
    
    <div class="back-top iconfont-archer">&#xe639;</div>
    <div class="sidebar sidebar-hide">
    <ul class="sidebar-tabs sidebar-tabs-active-0">
        <li class="sidebar-tab-archives"><span class="iconfont-archer">&#xe67d;</span><span class="tab-name">Archive</span></li>
        <li class="sidebar-tab-tags"><span class="iconfont-archer">&#xe61b;</span><span class="tab-name">Tag</span></li>
        <li class="sidebar-tab-categories"><span class="iconfont-archer">&#xe666;</span><span class="tab-name">Cate</span></li>
    </ul>
    <div class="sidebar-content sidebar-content-show-archive">
          <div class="sidebar-panel-archives">
    <!-- 在ejs中将archive按照时间排序 -->
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    <div class="total-and-search">
        <div class="total-archive">
        Total : 168
        </div>
        <!-- search  -->
        
    </div>
    
    <div class="post-archive">
    
    
    
    
    <div class="archive-year"> 2022 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/08</span><a class="archive-post-title" href= "/2022/01/08/K8s-leaderElection选主的写法套路/" >K8s leaderElection选主的写法套路</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2021 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">12/30</span><a class="archive-post-title" href= "/2021/12/30/k8s中如何远程执行pod命令/" >k8s中如何远程执行pod命令</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">12/01</span><a class="archive-post-title" href= "/2021/12/01/clientset中patch操作-策略性补丁之替换/" >clientset中patch操作-策略性补丁之替换</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">12/01</span><a class="archive-post-title" href= "/2021/12/01/fake-client的用法/" >fake-client的用法</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/25</span><a class="archive-post-title" href= "/2021/11/25/CRD开发中监控deployment的变化,并且显示在控制台/" >CRD开发中监控deployment的变化,并且显示在控制台</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/25</span><a class="archive-post-title" href= "/2021/11/25/k8s-reconcile中client如何使用patch/" >k8s reconcile中client如何使用patch</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/24</span><a class="archive-post-title" href= "/2021/11/24/k8s中如何发送http请求给api-server/" >k8s中如何发送http请求给api-server</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/17</span><a class="archive-post-title" href= "/2021/11/17/clientset中patch操作jsonpatch/" >clientset中patch操作:jsonpatch</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/29</span><a class="archive-post-title" href= "/2021/10/29/event的用法,-来显示资源的事件情况/" >event的用法, 来显示资源的事件情况</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/09</span><a class="archive-post-title" href= "/2021/10/09/dynamic-Client的使用方法/" >dynamic-Client的使用方法</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2020 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/15</span><a class="archive-post-title" href= "/2020/10/15/Operator介绍(1)/" >Operator介绍(1)</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/15</span><a class="archive-post-title" href= "/2020/10/15/Operator介绍(2)---以etcd-operator为例/" >Operator介绍(2)---以etcd-operator为例</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/15</span><a class="archive-post-title" href= "/2020/10/15/Operator SDK开发简介/" >Operator SDK开发简介</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/05</span><a class="archive-post-title" href= "/2020/01/05/kubernetes-的负载均衡策略/" >kubernetes 的负载均衡策略</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2019 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/01</span><a class="archive-post-title" href= "/2019/03/01/kubernetes DaemonSet是如何进行版本管理的/" >kubernetes DaemonSet是如何进行版本管理的</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/01</span><a class="archive-post-title" href= "/2019/03/01/kubernetes DaemonSet是如何保证pod只在一个节点上运行并且保证数量是1/" >kubernetes DaemonSet是如何保证pod只在一个节点上运行并且保证数量是1</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/01</span><a class="archive-post-title" href= "/2019/03/01/kubernetes deployment在滚动升级的时候如何控制新旧pod比例/" >kubernetes deployment在滚动升级的时候如何控制新旧pod比例</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/01</span><a class="archive-post-title" href= "/2019/03/01/kubernetes deployment如何控制每次更新操作只触发一个ReolicaSet以及设置保留历史个数版本/" >kubernetes deployment如何控制每次更新操作只触发一个ReolicaSet以及设置保留历史个数版本</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/01</span><a class="archive-post-title" href= "/2019/03/01/kubernetes中如何让pod指定声明使用某个特定的initializer/" >kubernetes中如何让pod指定声明使用某个特定的initializer</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/02</span><a class="archive-post-title" href= "/2019/01/02/CNCF云原生生态系统大览(CNCF-Cloud-Native-Interactive-Landscape)/" >构建企业级云服务生态指南---CNCF云原生生态系统大览</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/02</span><a class="archive-post-title" href= "/2019/01/02/StorageClass--kubernetes自动创建pv的机制(Dynamic-Provisioning)/" >StorageClass---kubernetes自动创建pv的机制(Dynamic Provisioning)</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2018 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">12/22</span><a class="archive-post-title" href= "/2018/12/22/kubernetes-EFK实践/" >kubernetes EFK实践</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">12/05</span><a class="archive-post-title" href= "/2018/12/05/kubelet是如何操作容器管理pod的/" >kubelet是如何操作容器管理pod的</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/10</span><a class="archive-post-title" href= "/2018/11/10/kubernetes CRD下篇--编写自定义控制器/" >kubernetes CRD下篇--编写自定义控制器</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/05</span><a class="archive-post-title" href= "/2018/11/05/kuberadm初始化的时候使用自定义镜像源方法/" >kuberadm初始化的时候使用自定义镜像源方法</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/03</span><a class="archive-post-title" href= "/2018/11/03/kubernetes中的Projected Volume是什么/" >kubernetes中的Projected Volume是什么</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/01</span><a class="archive-post-title" href= "/2018/11/01/kubernetes中的控制器模型-以Deployment的实现为例/" >kubernetes中的"控制器"模型-以Deployment的实现为例</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/14</span><a class="archive-post-title" href= "/2018/10/14/ginx中HTTP请求的11个阶段之第九阶段--precontent阶段/" >Nginx中HTTP请求的11个阶段之第九阶段--precontent阶段</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">06/27</span><a class="archive-post-title" href= "/2018/06/27/kubeadm的配置文件路径和意义/" >kubeadm的配置文件路径和意义</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">05/11</span><a class="archive-post-title" href= "/2018/05/11/heapster部署/" >heapster部署</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/27</span><a class="archive-post-title" href= "/2018/03/27/kubeadm init可能出现的问题/" >kubeadm init可能出现的问题</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2016 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">04/25</span><a class="archive-post-title" href= "/2016/04/25/hello-world/" >Hello World</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes  RBAC认证和serviceaccount/" >kubernetes认证和serviceaccount</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes ConfigMap/" >kubernetes ConfigMap</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes HPA/" >kubernetes HPA</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes StatefulSet/" >kubernetes StatefulSet</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes RBAC/" >kubernetes RBAC</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes dashboard部署/" >kubernetes dashboard部署</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes service实现方法和类型/" >kubernetes service实现方法和类型</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes ingress controller的配置和工作过程/" >kubernetes ingress controller的配置和工作过程</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2018 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/03</span><a class="archive-post-title" href= "/2018/11/03/kubernetes中api-server的地址改成公网ip和负载均衡/" >kubernetes中api-server的地址改成公网ip和负载均衡</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/02</span><a class="archive-post-title" href= "/2018/11/02/Kubernetes 存储插件项目：Rook/" >Kubernetes 存储插件项目：Rook</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/30</span><a class="archive-post-title" href= "/2018/10/30/Deployment 对应用进行版本控制的具体原理--以金丝雀发布（Canary Deployment）为例/" >Deployment 对应用进行版本控制的具体原理--以金丝雀发布（Canary Deployment）为例</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/15</span><a class="archive-post-title" href= "/2018/10/15/Nginx中HTTP请求的11个阶段之第七阶段--access阶段做权限控制,ip限制/" >Nginx中HTTP请求的11个阶段之第七阶段--access阶段做权限控制,ip限制</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/14</span><a class="archive-post-title" href= "/2018/10/14/Nginx中HTTP请求的11个阶段之第十阶段---content阶段(root和alias指令)/" >Nginx中HTTP请求的11个阶段之第十阶段---content阶段(root和alias指令)</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/13</span><a class="archive-post-title" href= "/2018/10/13/Nginx中HTTP请求的11个阶段之第十一个阶段---log阶段/" >Nginx中HTTP请求的11个阶段之第十一个阶段---log阶段</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/12</span><a class="archive-post-title" href= "/2018/10/12/Nginx中HTTP框架提供的变量/" >Nginx中HTTP框架提供的变量</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/10</span><a class="archive-post-title" href= "/2018/10/10/Nginx中HTTP请求的11个阶段之第一阶段----postread阶段/" >Nginx中HTTP请求的11个阶段之第一阶段----postread阶段(realip模块)</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/10</span><a class="archive-post-title" href= "/2018/10/10/Nginx中HTTP请求的11个阶段之第三阶段----find_config  (location指令)/" >Nginx中HTTP请求的11个阶段之第三阶段----find_config  (location指令)</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/10</span><a class="archive-post-title" href= "/2018/10/10/Nginx中HTTP请求的11个阶段之第二阶段和第四阶段---rewrite阶段/" >Nginx中HTTP请求的11个阶段之第二阶段和第四阶段---rewrite阶段</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/10</span><a class="archive-post-title" href= "/2018/10/10/Nginx中HTTP请求的11个阶段之第六阶段---PREACCESS (limit_req ,limit_conn模块)/" >Nginx中HTTP请求的11个阶段之第六阶段---PREACCESS (limit_req ,limit_conn模块)</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes中docker连到私有仓库认证问题/" >kubernetes中docker连到私有仓库认证问题</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2018 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">08/06</span><a class="archive-post-title" href= "/2018/08/06/kubernetes中的volumes挂载类型为emptydir,在宿主机上的路径在哪/" >kubernetes中的volumes挂载类型为emptydir,在宿主机上的路径在哪</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">08/01</span><a class="archive-post-title" href= "/2018/08/01/docker是如何创建容器的pid为1的进程的/" >docker是如何创建容器的pid为1的进程的</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">07/22</span><a class="archive-post-title" href= "/2018/07/22/kubernetes中kubeconfig是什么/" >kubernetes中kubeconfig是什么?</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">07/06</span><a class="archive-post-title" href= "/2018/07/06/docker是如何做到资源限制的/" >docker是如何做到资源限制的</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">06/22</span><a class="archive-post-title" href= "/2018/06/22/Helm/" >Helm</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/24</span><a class="archive-post-title" href= "/2018/03/24/docker exec 是怎么做到进入容器里的呢？/" >docker exec 是怎么做到进入容器里的呢？</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/11</span><a class="archive-post-title" href= "/2018/03/11/docker如何修复容器中的 top 指令以及 proc 文件系统中的信息呢/" >docker如何修复容器中的 top 指令以及 /proc 文件系统中的信息呢</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2017 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">07/07</span><a class="archive-post-title" href= "/2017/07/07/docker私有仓库--docker-distribution/" >docker私有仓库--docker-distribution</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">06/22</span><a class="archive-post-title" href= "/2017/06/22/Docker 容器明文密码解决的2个方法/" >Docker 容器明文密码解决的2个方法</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">06/11</span><a class="archive-post-title" href= "/2017/06/11/docker自定义docker0网桥网络属性/" >docker自定义docker0网桥网络属性</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">06/07</span><a class="archive-post-title" href= "/2017/06/07/dockerfile常用指令/" >dockerfile常用指令</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">06/07</span><a class="archive-post-title" href= "/2017/06/07/docker网络初探/" >docker网络初探</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">05/11</span><a class="archive-post-title" href= "/2017/05/11/docker网络/" >docker网络</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">05/09</span><a class="archive-post-title" href= "/2017/05/09/docker列出镜像用法/" >docker列出镜像用法</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">04/23</span><a class="archive-post-title" href= "/2017/04/23/docker相关命令--pull , commit ,save,load/" >docker相关命令--pull , commit ,save,load</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">04/07</span><a class="archive-post-title" href= "/2017/04/07/docker私有仓库--Harbor/" >docker私有仓库--Harbor</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/17</span><a class="archive-post-title" href= "/2017/03/17/docker相关机构/" >docker相关机构</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/11</span><a class="archive-post-title" href= "/2017/03/11/docker端口映射/" >docker端口映射</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">02/15</span><a class="archive-post-title" href= "/2017/02/15/docker存储卷/" >docker存储卷</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">02/11</span><a class="archive-post-title" href= "/2017/02/11/Docker Daemon 的作用是什么/" >Docker Daemon 的作用是什么</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">02/11</span><a class="archive-post-title" href= "/2017/02/11/docker image分层构建 联合挂载/" >docker</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/24</span><a class="archive-post-title" href= "/2017/01/24/docker安装/" >docker安装</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/21</span><a class="archive-post-title" href= "/2017/01/21/docker相关概念/" >docker相关概念</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/04</span><a class="archive-post-title" href= "/2017/01/04/docker官方设置http_proxy教程/" >docker官方设置http_proxy教程</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2016 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">05/19</span><a class="archive-post-title" href= "/2016/05/19/docker镜像加速的方法/" >docker镜像加速的方法</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes中查看pod内部的dns解析/" >kubernetes中查看pod内部的dns解析</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes中的认证---default-token/" >kubernetes中的认证---default-token</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes中的认证流程和资源请求/" >kubernetes中的认证流程和资源请求</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes中的账号/" >kubernetes中的账号</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes中资源yaml模板创建/" >kubernetes中资源yaml模板创建</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes中网络插件地址/" >kubernetes中网络插件地址</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes中进入pod运行命令/" >kubernetes进入pod方法</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes中部署prometheus/" >kubernetes中部署prometheus</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes中资源的引用方式/" >kubernetes中资源的引用方式</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes中部署一个服务用的简单模板/" >kubernetes中部署一个服务用的简单模板</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2021 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">12/28</span><a class="archive-post-title" href= "/2021/12/28/mgr.Add函数用法在mgr启动的时候会一起把这个对象也启动了/" >mgr.Add函数用法:在mgr启动的时候会一起把这个对象也启动了</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">12/27</span><a class="archive-post-title" href= "/2021/12/27/mgr中开启控制器选主/" >mgr中开启控制器选主</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2019 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">09/10</span><a class="archive-post-title" href= "/2019/09/10/kubernetes开发教程(4)自动代码生成/" >kubernetes开发教程(4)--自动代码生成</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">09/08</span><a class="archive-post-title" href= "/2019/09/08/kuberntes开发教程系列(3)了解和编写Custom Resources/" >kuberntes开发教程系列(3)--了解和编写Custom Resources</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">09/07</span><a class="archive-post-title" href= "/2019/09/07/kuberntes开发教程系列(2)  了解client-go是什么/" >kuberntes开发教程系列(2):了解client-go是什么</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">09/06</span><a class="archive-post-title" href= "/2019/09/06/kuberntes开发教程系列(一)认识kuberntes API/" >kuberntes开发教程系列(1):认识kuberntes API</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">06/24</span><a class="archive-post-title" href= "/2019/06/24/kubernetes持久化存储卷存储卷快照Snapshot/" >kubernetes持久化存储卷:存储卷快照Snapshot详解</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/10</span><a class="archive-post-title" href= "/2019/01/10/kubernetes的本地持久化存储--Local Persistent Volume解析/" >kubernetes的本地持久化存储--Local Persistent Volume解析</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/02</span><a class="archive-post-title" href= "/2019/01/02/kubernetes挂载卷的基本过程/" >kubernetes挂载卷的基本过程</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2018 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">12/21</span><a class="archive-post-title" href= "/2018/12/21/kubernetes容器日志收集方案/" >kubernetes容器日志收集方案</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/27</span><a class="archive-post-title" href= "/2018/11/27/kubernetes怎么修改与设置触发资源回收策略的默认值/" >kubernetes怎么修改与设置触发资源回收策略的默认值</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/26</span><a class="archive-post-title" href= "/2018/11/26/kubernetes中默认调度器是如何调度一个pod的到节点上的/" >kubernetes中默认调度器的调度流程是怎么进行的</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/16</span><a class="archive-post-title" href= "/2018/11/16/kubernetes中默认为pod创建的volume目录是在宿主机的什么路径/" >kubernetes中默认为pod创建的volume目录是在宿主机的什么路径</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/10</span><a class="archive-post-title" href= "/2018/11/10/kubernetes动态准入控制Initializers是什么/" >kubernetes动态准入控制Initializers是什么</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/10</span><a class="archive-post-title" href= "/2018/11/10/kubernetes是如何创建api对象的/" >kubernetes是如何创建api对象的</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/15</span><a class="archive-post-title" href= "/2018/10/15/nginx中upstream模块提供的变量(不含cache)/" >nginx中upstream模块提供的变量(不含cache)</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/15</span><a class="archive-post-title" href= "/2018/10/15/nginx中缓存用法和优化/" >nginx中缓存用法和优化</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/05</span><a class="archive-post-title" href= "/2018/10/05/nginx架构基础相关知识/" >nginx架构基础相关知识</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/05</span><a class="archive-post-title" href= "/2018/01/05/kubernetes初探/" >kubernetes初探</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes如何创建被认证的用户/" >kubernetes如何创建被认证的用户</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes存储卷/" >kubernetes存储卷</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes访问集群节点的资源/" >kubernetes访问集群节点的资源</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes组件/" >kubernetes组件</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2018 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">09/28</span><a class="archive-post-title" href= "/2018/09/28/kubernetes调度pod策略/" >kubernetes调度pod策略</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes资源定义清单中的apiVersion说明/" >kubernetes资源定义清单中的apiVersion说明</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes资源指标metrics-server/" >kubernetes资源指标metrics-server</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes资源清单/" >kubernetes资源清单</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2018 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/06</span><a class="archive-post-title" href= "/2018/11/06/kubernetes部署dashboard(nginx-ingress模式访问)/" >kubernetes部署dashboard(nginx-ingress模式访问)</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/20</span><a class="archive-post-title" href= "/2018/10/20/kubernetes部署metrics-server(2)/" >kubernetes部署metrics-server(2)</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes集群的外部地址和内部地址/" >kubernetes集群的外部地址和内部地址</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes高级调度方式/" >kubernetes高级调度方式</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2018 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/15</span><a class="archive-post-title" href= "/2018/10/15/nginx中的keepalive , 共享内存/" >nginx中的keepalive , 共享内存</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/15</span><a class="archive-post-title" href= "/2018/10/15/nginx使用open_file_cache提升系统性能/" >nginx使用open_file_cache提升系统性能</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/10</span><a class="archive-post-title" href= "/2018/10/10/nginx中的http反向代理流程及proxy模块/" >nginx中的http反向代理流程及proxy模块</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2016 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/11</span><a class="archive-post-title" href= "/2016/03/11/linux的五种IO模型/" >linux的五种IO模型</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/kubernetes资源限制/" >kubernetes资源限制</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> Invalid date </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">Invalid date</span><a class="archive-post-title" href= "/2019/09/02/nginx-ingress/" >一个nginx-ingress部署示例</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2022 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/22</span><a class="archive-post-title" href= "/2022/03/22/取任意yaml资源转化为k8s资源的完全通用写法(kubectl源码的写法)/" >取任意yaml资源转化为k8s资源的完全通用写法(kubectl源码的写法)</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/05</span><a class="archive-post-title" href= "/2022/01/05/如何解析caCert,caPrikey/" >如何解析caCert,caPrikey</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/05</span><a class="archive-post-title" href= "/2022/01/05/代码自动签发证书/" >代码自动签发证书</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/03</span><a class="archive-post-title" href= "/2022/01/03/写个通用代码来模拟kubectl-apply文件/" >写个通用代码来模拟kubectl apply文件(kustomize模式)</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">01/01</span><a class="archive-post-title" href= "/2022/01/01/如何代码创建自定义用户的config文件/" >如何代码创建自定义用户的config文件</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2021 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">12/29</span><a class="archive-post-title" href= "/2021/12/29/如何通过grk来找gvr,或者gvr找gvk/" >如何通过grk来找gvr,或者gvr找gvk</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/26</span><a class="archive-post-title" href= "/2021/11/26/代码获取pod的日志/" >代码获取pod的日志</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/25</span><a class="archive-post-title" href= "/2021/11/25/获取资源api-reasources/" >获取资源api-reasources</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/15</span><a class="archive-post-title" href= "/2021/11/15/自定义controller如何重新拉起被删除的资源/" >自定义controller如何重新拉起被删除的资源</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/13</span><a class="archive-post-title" href= "/2021/11/13/判断某个rs是否从属于某个deployment/" >判断某个rs是否从属于某个deployment</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/05</span><a class="archive-post-title" href= "/2021/11/05/命令行中如何取到serviceaccout的token/" >命令行中如何取到serviceaccout的token</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/05</span><a class="archive-post-title" href= "/2021/11/05/如何配置token来访问api-server/" >如何配置token来访问api-server</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/25</span><a class="archive-post-title" href= "/2021/10/25/通过labelSelector来选择pod操作/" >通过labelSelector来选择pod操作</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/17</span><a class="archive-post-title" href= "/2021/10/17/初始化控制器controller-runtime中mgr的例子/" >初始化控制器controller-runtime中mgr的例子</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">09/29</span><a class="archive-post-title" href= "/2021/09/29/生成config和client的几种写法/" >生成config和client的几种写法</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">09/17</span><a class="archive-post-title" href= "/2021/09/17/强制删除ns/" >强制删除ns</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">09/10</span><a class="archive-post-title" href= "/2021/09/10/reconcile的时候如何判断一个资源是否已经存在/" >reconcile的时候如何判断一个资源是否已经存在</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">07/05</span><a class="archive-post-title" href= "/2021/07/05/创建config和clientset/" >创建config和clientset</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2020 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/22</span><a class="archive-post-title" href= "/2020/10/22/创建k8s用户账号/" >创建k8s用户账号</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">09/29</span><a class="archive-post-title" href= "/2020/09/29/如何级联删除资源/" >如何级联删除资源</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">05/17</span><a class="archive-post-title" href= "/2020/05/17/使用临时调试容器来进行调试/" >使用临时调试容器来进行调试</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2019 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/17</span><a class="archive-post-title" href= "/2019/10/17/如何取出kubectl结果里面的pod名称/" >如何取出kubectl结果里面的pod名称</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2018 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">12/03</span><a class="archive-post-title" href= "/2018/12/03/yaml转json/" >yaml转json</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">12/03</span><a class="archive-post-title" href= "/2018/12/03/在kubernetes中如何增加和使用node节点上的自定义资源数量/" >在kubernetes中如何增加和使用node节点上的自定义资源数量</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/25</span><a class="archive-post-title" href= "/2018/11/25/在kubernetes中如何让pod绑定在某颗cpu上/" >在kubernetes中如何让pod绑定在某颗cpu上</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/25</span><a class="archive-post-title" href= "/2018/11/25/当在kubernetes中触发了资源回收策略的时候, pod删除的序列是什么/" >当在kubernetes中触发了资源回收策略的时候, pod删除的序列是什么</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/15</span><a class="archive-post-title" href= "/2018/11/15/基于canel的网络策略/" >基于canel的网络策略</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/10</span><a class="archive-post-title" href= "/2018/11/10/在kubernetes中如何添加CRD(Custom Resource Definition)/" >在kubernetes中如何添加CRD(Custom Resource Definition)(1)</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/07</span><a class="archive-post-title" href= "/2018/11/07/prometheus报警--------通过slack发送报警内容/" >prometheus报警--------通过slack发送报警内容</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/02</span><a class="archive-post-title" href= "/2018/11/02/在kubernetes中如何给pod自动填充某些字段/" >在kubernetes中如何给pod自动填充某些默认字段</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/15</span><a class="archive-post-title" href= "/2018/10/15/nginx中的负载均衡算法/" >nginx中的负载均衡算法</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">10/12</span><a class="archive-post-title" href= "/2018/10/12/利用TravisCI同步gcr.io镜像到dockerhub/" >利用TravisCI同步gcr.io镜像到dockerhub</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">09/25</span><a class="archive-post-title" href= "/2018/09/25/部署一个有证书的ingress-nginx/" >部署一个有证书的ingress-nginx</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">08/08</span><a class="archive-post-title" href= "/2018/08/08/prometheus安装/" >prometheus安装</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">05/03</span><a class="archive-post-title" href= "/2018/05/03/理解docker的rootfs和分层构建联合挂载的概念/" >理解docker的rootfs和分层构建联合挂载的概念</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/29</span><a class="archive-post-title" href= "/2018/03/29/容器内配置文件和环境变量,参数的传递/" >容器内配置文件和环境变量,参数的传递</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">03/11</span><a class="archive-post-title" href= "/2018/03/11/解决kubeadm安装k8s的时候images不能下载的问题--方法2/" >解决kubeadm安装k8s的时候images不能下载的问题--方法2</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">02/13</span><a class="archive-post-title" href= "/2018/02/13/为什么kubernetes集群上master不会被调度/" >为什么kubernetes集群上master不会被调度</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">02/12</span><a class="archive-post-title" href= "/2018/02/12/使用kubeadm从0开始搭建kubernetesv1.11.1/" >使用kubeadm从0开始搭建kubernetes:v1.11.1</a>
        </li>
    
    
    
    
    
        </ul>
    
    <div class="archive-year"> 2017 </div>
    <ul class="year-list">
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">11/07</span><a class="archive-post-title" href= "/2017/11/07/tcpdump nc nmap/" >tcpdump nc nmap</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">09/22</span><a class="archive-post-title" href= "/2017/09/22/使用ssh进行git登陆和推送/" >使用ssh进行git登陆和推送</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">06/07</span><a class="archive-post-title" href= "/2017/06/07/用entrypoint脚本来初始化docker自定义配置文件-以nginx为例/" >用entrypoint脚本来初始化docker自定义配置文件-以nginx为例</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">05/25</span><a class="archive-post-title" href= "/2017/05/25/容器发展史和docker容器核心技术--chroot ,namespace和cgroups/" >容器核心技术--chroot ,namespace,cgroups,LCX,和docker</a>
        </li>
    
    
        <li class="archive-post-item">
            <span class="archive-post-date">02/05</span><a class="archive-post-title" href= "/2017/02/05/国内docker hub的加速镜像服务/" >国内docker hub的加速镜像服务</a>
        </li>
    
    </div>
  </div>
        <div class="sidebar-panel-tags">
    <div class="sidebar-tags-name">
    
        <span class="sidebar-tag-name" data-tags="kubernetes"><span class="iconfont-archer">&#xe606;</span>kubernetes</span>
    
        <span class="sidebar-tag-name" data-tags="docker"><span class="iconfont-archer">&#xe606;</span>docker</span>
    
        <span class="sidebar-tag-name" data-tags="helm"><span class="iconfont-archer">&#xe606;</span>helm</span>
    
        <span class="sidebar-tag-name" data-tags="tips"><span class="iconfont-archer">&#xe606;</span>tips</span>
    
        <span class="sidebar-tag-name" data-tags="harbor"><span class="iconfont-archer">&#xe606;</span>harbor</span>
    
        <span class="sidebar-tag-name" data-tags="docker网络"><span class="iconfont-archer">&#xe606;</span>docker网络</span>
    
        <span class="sidebar-tag-name" data-tags="heapster"><span class="iconfont-archer">&#xe606;</span>heapster</span>
    
        <span class="sidebar-tag-name" data-tags="serviceaccount"><span class="iconfont-archer">&#xe606;</span>serviceaccount</span>
    
        <span class="sidebar-tag-name" data-tags="configmap"><span class="iconfont-archer">&#xe606;</span>configmap</span>
    
        <span class="sidebar-tag-name" data-tags="HPA"><span class="iconfont-archer">&#xe606;</span>HPA</span>
    
        <span class="sidebar-tag-name" data-tags="kbuernetes statefulset"><span class="iconfont-archer">&#xe606;</span>kbuernetes statefulset</span>
    
        <span class="sidebar-tag-name" data-tags="RBAC"><span class="iconfont-archer">&#xe606;</span>RBAC</span>
    
        <span class="sidebar-tag-name" data-tags="kubernetes service"><span class="iconfont-archer">&#xe606;</span>kubernetes service</span>
    
        <span class="sidebar-tag-name" data-tags="ingress"><span class="iconfont-archer">&#xe606;</span>ingress</span>
    
        <span class="sidebar-tag-name" data-tags="prometheus"><span class="iconfont-archer">&#xe606;</span>prometheus</span>
    
        <span class="sidebar-tag-name" data-tags="kubernetes 存储卷"><span class="iconfont-archer">&#xe606;</span>kubernetes 存储卷</span>
    
        <span class="sidebar-tag-name" data-tags="kubernetes资源"><span class="iconfont-archer">&#xe606;</span>kubernetes资源</span>
    
        <span class="sidebar-tag-name" data-tags="kubernetes调度"><span class="iconfont-archer">&#xe606;</span>kubernetes调度</span>
    
        <span class="sidebar-tag-name" data-tags="linux base"><span class="iconfont-archer">&#xe606;</span>linux base</span>
    
        <span class="sidebar-tag-name" data-tags="ingress-nginx"><span class="iconfont-archer">&#xe606;</span>ingress-nginx</span>
    
        <span class="sidebar-tag-name" data-tags="Linux"><span class="iconfont-archer">&#xe606;</span>Linux</span>
    
        <span class="sidebar-tag-name" data-tags="kubernetes安装"><span class="iconfont-archer">&#xe606;</span>kubernetes安装</span>
    
        <span class="sidebar-tag-name" data-tags="git"><span class="iconfont-archer">&#xe606;</span>git</span>
    
        <span class="sidebar-tag-name" data-tags="travis"><span class="iconfont-archer">&#xe606;</span>travis</span>
    
        <span class="sidebar-tag-name" data-tags="gcr.io镜像"><span class="iconfont-archer">&#xe606;</span>gcr.io镜像</span>
    
        <span class="sidebar-tag-name" data-tags="kubernetes网络策略"><span class="iconfont-archer">&#xe606;</span>kubernetes网络策略</span>
    
    </div>
    <div class="iconfont-archer sidebar-tags-empty">&#xe678;</div>
    <div class="tag-load-fail" style="display: none; color: #ccc; font-size: 0.6rem;">
    缺失模块。<br/>
    1、请确保node版本大于6.2<br/>
    2、在博客根目录（注意不是archer根目录）执行以下命令：<br/>
    <span style="color: #f75357; font-size: 1rem; line-height: 2rem;">npm i hexo-generator-json-content --save</span><br/>
    3、在根目录_config.yml里添加配置：
    <pre style="color: #787878; font-size: 0.6rem;">
jsonContent:
  meta: false
  pages: false
  posts:
    title: true
    date: true
    path: true
    text: false
    raw: false
    content: false
    slug: false
    updated: false
    comments: false
    link: false
    permalink: false
    excerpt: false
    categories: true
    tags: true</pre>
    </div> 
    <div class="sidebar-tags-list"></div>
</div>
        <div class="sidebar-panel-categories">
    <div class="sidebar-categories-name">
    
        <span class="sidebar-category-name" data-categories="kubernetes"><span class="iconfont-archer">&#xe60a;</span>kubernetes</span>
    
        <span class="sidebar-category-name" data-categories="tips"><span class="iconfont-archer">&#xe60a;</span>tips</span>
    
        <span class="sidebar-category-name" data-categories="docker"><span class="iconfont-archer">&#xe60a;</span>docker</span>
    
        <span class="sidebar-category-name" data-categories="nginx"><span class="iconfont-archer">&#xe60a;</span>nginx</span>
    
        <span class="sidebar-category-name" data-categories="docker/tips"><span class="iconfont-archer">&#xe60a;</span>docker/tips</span>
    
        <span class="sidebar-category-name" data-categories="kuberntes"><span class="iconfont-archer">&#xe60a;</span>kuberntes</span>
    
        <span class="sidebar-category-name" data-categories="linux"><span class="iconfont-archer">&#xe60a;</span>linux</span>
    
        <span class="sidebar-category-name" data-categories="Linux-Base"><span class="iconfont-archer">&#xe60a;</span>Linux-Base</span>
    
        <span class="sidebar-category-name" data-categories="tips/docker"><span class="iconfont-archer">&#xe60a;</span>tips/docker</span>
    
    </div>
    <div class="iconfont-archer sidebar-categories-empty">&#xe678;</div>
    <div class="sidebar-categories-list"></div>
</div>
    </div>
</div> 
    <script>
    var siteMeta = {
        root: "/",
        author: "HAOJX"
    }
</script>
    <!-- CDN failover -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script>
    <script type="text/javascript">
        if (typeof window.$ === 'undefined')
        {
            console.warn('jquery load from jsdelivr failed, will load local script')
            document.write('<script src="/lib/jquery.min.js">\x3C/script>')
        }
    </script>
    <script src="/scripts/main.js"></script>
    <!-- algolia -->
    
    <!-- busuanzi  -->
    
    <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    
    <!-- CNZZ  -->
    
    </div>
    <!-- async load share.js -->
    
        <script src="/scripts/share.js" async></script>    
     
    <script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"pluginRootPath":"live2dw/","pluginJsPath":"lib/","pluginModelPath":"assets/","tagMode":false,"debug":false,"model":{"jsonPath":"live2d-widget-model-tororo"},"display":{"position":"left","width":150,"height":300},"mobile":{"show":true},"log":false});</script></body>
</html>


